Browse Source

first code commit.

Ugochukwu Mmaduekwe 7 years ago
parent
commit
e3be3239dc
29 changed files with 4744 additions and 1 deletions
  1. 42 0
      .gitignore
  2. 159 0
      .travis.install.py
  3. 74 0
      .travis.yml
  4. 1 1
      LICENSE
  5. 36 0
      QRCodeGenLib.Demo/Delphi.Demo/QrCodeGeneratorDemo.dpr
  6. 69 0
      QRCodeGenLib.Demo/FreePascal.Demo/QrCodeGeneratorDemo.lpi
  7. 26 0
      QRCodeGenLib.Demo/FreePascal.Demo/QrCodeGeneratorDemo.lpr
  8. 319 0
      QRCodeGenLib.Demo/src/uQrCodeGeneratorDemo.pas
  9. 132 0
      QRCodeGenLib/src/Include/QRCodeGenLib.inc
  10. 22 0
      QRCodeGenLib/src/Include/QRCodeGenLibHelper.inc
  11. 178 0
      QRCodeGenLib/src/Interfaces/QlpIQrCode.pas
  12. 28 0
      QRCodeGenLib/src/Interfaces/QlpIQrSegment.pas
  13. 26 0
      QRCodeGenLib/src/Interfaces/QlpIQrTemplate.pas
  14. 20 0
      QRCodeGenLib/src/Interfaces/QlpIReedSolomonGenerator.pas
  15. 116 0
      QRCodeGenLib/src/Packages/FPC/QRCodeGenLib4PascalPackage.lpk
  16. 18 0
      QRCodeGenLib/src/Packages/FPC/QRCodeGenLib4PascalPackage.pas
  17. 257 0
      QRCodeGenLib/src/QRCodeGen/QlpBitBuffer.pas
  18. 1317 0
      QRCodeGenLib/src/QRCodeGen/QlpQrCode.pas
  19. 48 0
      QRCodeGenLib/src/QRCodeGen/QlpQrCodeCommons.pas
  20. 561 0
      QRCodeGenLib/src/QRCodeGen/QlpQrSegment.pas
  21. 110 0
      QRCodeGenLib/src/QRCodeGen/QlpQrSegmentMode.pas
  22. 555 0
      QRCodeGenLib/src/QRCodeGen/QlpQrTemplate.pas
  23. 225 0
      QRCodeGenLib/src/QRCodeGen/QlpReedSolomonGenerator.pas
  24. 92 0
      QRCodeGenLib/src/Utils/QlpArrayUtils.pas
  25. 69 0
      QRCodeGenLib/src/Utils/QlpBits.pas
  26. 53 0
      QRCodeGenLib/src/Utils/QlpConverters.pas
  27. 42 0
      QRCodeGenLib/src/Utils/QlpGuard.pas
  28. 98 0
      QRCodeGenLib/src/Utils/QlpQRCodeGenLibTypes.pas
  29. 51 0
      README.md

+ 42 - 0
.gitignore

@@ -1,3 +1,7 @@
+
+# Created by https://www.gitignore.io/api/delphi,lazarus,freepascal
+
+### Delphi ###
 # Uncomment these types if you want even more clean repository. But be careful.
 # It can make harm to an existing project source. Read explanations below.
 #
@@ -64,3 +68,41 @@ __recovery/
 
 # Castalia statistics file (since XE7 Castalia is distributed with Delphi)
 *.stat
+
+### FreePascal ###
+*.lps
+*.compiled
+*.[oa]
+*.ppu
+*.rst
+*.cgi
+*.log
+*.bak*
+fp.ini
+fp.cfg
+fp.dsk
+### Lazarus ###
+# Lazarus compiler-generated binaries (safe to delete)
+*.dylib
+*.lrs
+*.res
+*.dbg
+*.or
+
+# Lazarus autogenerated files (duplicated info)
+*.rsj
+*.lrt
+
+# Lazarus local files (user-specific info)
+
+# Lazarus backups and unit output folders.
+# These can be changed by user in Lazarus/project options.
+backup/
+*.bak
+lib/
+
+# Application bundle for Mac OS
+*.app/
+
+
+# End of https://www.gitignore.io/api/delphi,lazarus,freepascal

+ 159 - 0
.travis.install.py

@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# Part of `travis-lazarus` (https://github.com/nielsAD/travis-lazarus)
+# License: MIT
+
+import sys
+import os
+import subprocess
+
+OS_NAME=os.environ.get('TRAVIS_OS_NAME') or 'linux'
+OS_PMAN={'linux': 'sudo apt-get', 'osx': 'brew'}[OS_NAME]
+
+LAZ_TMP_DIR=os.environ.get('LAZ_TMP_DIR') or 'lazarus_tmp'
+LAZ_REL_DEF=os.environ.get('LAZ_REL_DEF') or {'linux':'amd64', 'qemu-arm':'amd64', 'qemu-arm-static':'amd64', 'osx':'i386', 'wine':'32'}
+LAZ_BIN_SRC=os.environ.get('LAZ_BIN_SRC') or 'http://mirrors.iwi.me/lazarus/releases/%(target)s/Lazarus%%20%(version)s'
+LAZ_BIN_TGT=os.environ.get('LAZ_BIN_TGT') or {
+    'linux':           'Lazarus%%20Linux%%20%(release)s%%20DEB',
+    'qemu-arm':        'Lazarus%%20Linux%%20%(release)s%%20DEB',
+    'qemu-arm-static': 'Lazarus%%20Linux%%20%(release)s%%20DEB',
+    'osx':             'Lazarus%%20Mac%%20OS%%20X%%20%(release)s',
+    'wine':            'Lazarus%%20Windows%%20%(release)s%%20bits'
+}
+
+def install_osx_dmg(dmg):
+    try:
+        # Mount .dmg file and parse (automatically determined) target volumes
+        res = subprocess.check_output('sudo hdiutil attach %s | grep /Volumes/' % (dmg), shell=True)
+        vol = ('/Volumes/' + l.strip().split('/Volumes/')[-1] for l in res.splitlines() if '/Volumes/' in l)
+    except:
+        return False
+
+    # Install .pkg files with installer
+    install_pkg = lambda v, f: os.system('sudo installer -pkg %s/%s -target /' % (v, f)) == 0
+
+    for v in vol:
+        try:
+            if not all(map(lambda f: (not f.endswith('.pkg')) or install_pkg(v, f), os.listdir(v))):
+                return False
+        finally:
+            # Unmount after installation
+            os.system('hdiutil detach %s' % (v))
+
+    return True
+
+def install_lazarus_default():
+    if OS_NAME == 'linux':
+        # Make sure nogui is installed for headless runs
+        pkg = 'lazarus lcl-nogui'
+    elif OS_NAME == 'osx':
+        # Install brew cask first
+        pkg = 'fpc caskroom/cask/brew-cask && %s cask install fpcsrc lazarus' % (OS_PMAN)
+    else:
+        # Default to lazarus
+        pkg = 'lazarus'
+    return os.system('%s install %s' % (OS_PMAN, pkg)) == 0
+
+def install_lazarus_version(ver,rel,env):
+    # Download all files in directory for specified Lazarus version
+    osn = env or OS_NAME
+    tgt = LAZ_BIN_TGT[osn] % {'release': rel or LAZ_REL_DEF[osn]}
+    src = LAZ_BIN_SRC % {'target': tgt, 'version': ver}
+    if os.system('wget -r -l1 -T 30 -np -nd -nc -A .deb,.dmg,.exe %s -P %s' % (src, LAZ_TMP_DIR)) != 0:
+        return False
+
+    if osn == 'wine':
+        # Install wine and Xvfb
+        if os.system('sudo dpkg --add-architecture i386 && %s update && %s install xvfb wine' % (OS_PMAN, OS_PMAN)) != 0:
+            return False
+
+        # Initialize virtual display and wine directory
+        if os.system('Xvfb %s & sleep 3 && wineboot -i' % (os.environ.get('DISPLAY') or '')) != 0:
+            return False
+
+        # Install basic Wine prerequisites, ignore failure
+        os.system('winetricks -q corefonts')
+
+        # Install all .exe files with wine
+        process_file = lambda f: (not f.endswith('.exe')) or os.system('wine %s /VERYSILENT /DIR="c:\\lazarus"' % (f)) == 0
+    elif osn == 'qemu-arm' or osn == 'qemu-arm-static':
+        # Install qemu and arm cross compiling utilities
+        if os.system('%s install libgtk2.0-dev qemu-user qemu-user-static binutils-arm-linux-gnueabi gcc-arm-linux-gnueabi' % (OS_PMAN)) != 0:
+            return False
+
+        # Install all .deb files (for linux) and cross compile later
+        process_file = lambda f: (not f.endswith('.deb')) or os.system('sudo dpkg --force-overwrite -i %s' % (f)) == 0
+    elif osn == 'linux':
+        # Install dependencies
+        if os.system('%s install libgtk2.0-dev' % (OS_PMAN)) != 0:
+            return False
+
+        # Install all .deb files
+        process_file = lambda f: (not f.endswith('.deb')) or os.system('sudo dpkg --force-overwrite -i %s' % (f)) == 0
+    elif osn == 'osx':
+        # Install all .dmg files
+        process_file = lambda f: (not f.endswith('.dmg')) or install_osx_dmg(f)
+    else:
+        return False
+
+    # Process all downloaded files
+    if not all(map(lambda f: process_file(os.path.join(LAZ_TMP_DIR, f)), sorted(os.listdir(LAZ_TMP_DIR)))):
+        return False
+
+    if osn == 'wine':
+        # Set wine Path (persistently) to include Lazarus binary directory
+        if os.system('wine cmd /C reg add HKEY_CURRENT_USER\\\\Environment /v PATH /t REG_SZ /d "%PATH%\\;c:\\\\lazarus"') != 0:
+            return False
+
+        # Redirect listed executables so they execute in wine
+        for alias in ('fpc', 'lazbuild', 'lazarus'):
+            os.system('echo "#!/usr/bin/env bash \nwine %(target)s \$@" | sudo tee %(name)s > /dev/null && sudo chmod +x %(name)s' % {
+                'target': subprocess.check_output("find $WINEPREFIX -iname '%s.exe' | head -1 " % (alias), shell=True).strip(),
+                'name': '/usr/bin/%s' % (alias)
+            })
+    elif osn == 'qemu-arm' or osn == 'qemu-arm-static':
+        fpcv = subprocess.check_output('fpc -iV', shell=True).strip()
+        gccv = subprocess.check_output('arm-linux-gnueabi-gcc -dumpversion', shell=True).strip()
+        opts = ' '.join([
+            'CPU_TARGET=arm',
+            'OS_TARGET=linux',
+            'BINUTILSPREFIX=arm-linux-gnueabi-',
+            # 'CROSSOPT="-CpARMV7A -CfVFPV3_D16"',
+            'OPT=-dFPC_ARMEL',
+            'INSTALL_PREFIX=/usr'
+        ])
+
+        # Compile ARM cross compiler
+        if os.system('cd /usr/share/fpcsrc/%s && sudo make clean crossall crossinstall %s' % (fpcv, opts)) != 0:
+            return False
+        
+        # Symbolic link to update default FPC cross compiler for ARM
+        if os.system('sudo ln -sf /usr/lib/fpc/%s/ppcrossarm /usr/bin/ppcarm' % (fpcv)) != 0:
+            return False
+
+        # Update config file with paths to ARM libraries
+        config = '\n'.join([
+            '#INCLUDE /etc/fpc.cfg',
+            '#IFDEF CPUARM',
+            '-Xd','-Xt',
+            '-XParm-linux-gnueabi-',
+            '-Fl/usr/arm-linux-gnueabi/lib',
+            '-Fl/usr/lib/gcc/arm-linux-gnueabi/%s' % (gccv),
+            '-Fl/usr/lib/gcc-cross/arm-linux-gnueabi/%s' % (gccv),
+            # '-CpARMV7A', '-CfVFPV3_D16',
+            '#ENDIF',
+            ''
+        ])
+        with open(os.path.expanduser('~/.fpc.cfg'),'w') as f:
+            f.write(config)
+
+    return True
+
+def install_lazarus(ver=None,rel=None,env=None):
+    return install_lazarus_version(ver,rel,env) if ver else install_lazarus_default()
+
+def main():
+    os.system('%s update' % (OS_PMAN))
+    return install_lazarus(os.environ.get('LAZ_VER'),os.environ.get('LAZ_REL'),os.environ.get('LAZ_ENV'))
+
+if __name__ == '__main__':
+    sys.exit(int(not main()))

+ 74 - 0
.travis.yml

@@ -0,0 +1,74 @@
+# Part of `travis-lazarus` (https://github.com/nielsAD/travis-lazarus)
+# License: MIT
+
+language: generic
+sudo: required
+dist: trusty
+
+os:
+  - linux
+  - osx
+
+env:
+  global:
+    - WINEPREFIX=~/.winelaz
+    - DISPLAY=:99.0
+  matrix:
+   # - LAZ_PKG=true  # Use the latest version from the default package manager
+    - LAZ_VER=1.6 # Use specific (binary) release
+    - LAZ_VER=1.8.0
+    - LAZ_VER=1.8.4
+   # - LAZ_VER=2.0RC1
+
+matrix:
+  include:
+    - os: linux
+      env: LAZ_VER=1.6  LAZ_ENV=wine WINEARCH=win32 LAZ_OPT="--os=win32 --cpu=i386"
+    - os: linux
+      env: LAZ_VER=1.8.0 LAZ_ENV=wine WINEARCH=win32 LAZ_OPT="--os=win32 --cpu=i386"
+    - os: linux
+      env: LAZ_VER=1.8.4 LAZ_ENV=wine WINEARCH=win32 LAZ_OPT="--os=win32 --cpu=i386"
+   # - os: linux
+   #   env: LAZ_VER=2.0RC1 LAZ_ENV=wine WINEARCH=win32 LAZ_OPT="--os=win32 --cpu=i386"
+    - os: linux
+      env: LAZ_VER=1.6  LAZ_ENV=wine WINEARCH=win64 LAZ_OPT="--os=win64 --cpu=x86_64"
+    - os: linux
+      env: LAZ_VER=1.8.0 LAZ_ENV=wine WINEARCH=win64 LAZ_OPT="--os=win64 --cpu=x86_64"
+    - os: linux
+      env: LAZ_VER=1.8.4 LAZ_ENV=wine WINEARCH=win64 LAZ_OPT="--os=win64 --cpu=x86_64"
+   # - os: linux
+   #   env: LAZ_VER=2.0RC1 LAZ_ENV=wine WINEARCH=win64 LAZ_OPT="--os=win64 --cpu=x86_64"
+    - os: linux
+      env: LAZ_VER=1.6  LAZ_ENV=qemu-arm LAZ_OPT="--os=linux --cpu=arm"
+    - os: linux
+      env: LAZ_VER=1.8.0 LAZ_ENV=qemu-arm LAZ_OPT="--os=linux --cpu=arm"
+    - os: linux
+      env: LAZ_VER=1.8.4 LAZ_ENV=qemu-arm LAZ_OPT="--os=linux --cpu=arm"
+   # - os: linux
+   #   env: LAZ_VER=2.0RC1 LAZ_ENV=qemu-arm LAZ_OPT="--os=linux --cpu=arm"
+
+before_install:
+  # Start virtual display server
+  - Xvfb $DISPLAY &
+  - if [ "$TRAVIS_OS_NAME" == "linux" ]; then
+          sudo apt-get update;
+          sudo apt-get install binutils-2.26;
+          sudo ln -sf /usr/lib/binutils-2.26/bin/* /usr/bin/;
+          sudo ln -sf /usr/lib/binutils-2.26/ldscripts/* /usr/lib/ldscripts/;
+    fi
+  - chmod +x .travis.install.py 
+
+install:
+  # Install prerequisites (fpc/lazarus/wine/qemu)
+  - ./.travis.install.py
+
+script:
+  - lazbuild --add-package-link ./QRCodeGenLib/src/Packages/FPC/QRCodeGenLib4PascalPackage.lpk  # Add QRCodeGenLib4Pascal Package
+  - lazbuild $LAZ_OPT ./QRCodeGenLib/src/Packages/FPC/QRCodeGenLib4PascalPackage.lpk  # Build QRCodeGenLib4Pascal Package
+  - lazbuild $LAZ_OPT ./QRCodeGenLib.Demo/FreePascal.Demo/QrCodeGeneratorDemo.lpi  # Build QRCodeGenLib4Pascal Demo Project
+  - travis_wait 120 $LAZ_ENV ./QRCodeGenLib.Demo/FreePascal.Demo/bin/QrCodeGeneratorDemo # Run QRCodeGenLib4Pascal Demo with timeout of 120 mins
+
+notifications:
+  email:
+    on_success: false
+    on_failure: change

+ 1 - 1
LICENSE

@@ -1,4 +1,4 @@
-MIT License
+The MIT License (MIT)
 
 Copyright (c) 2018 Ugochukwu Mmaduekwe
 

+ 36 - 0
QRCodeGenLib.Demo/Delphi.Demo/QrCodeGeneratorDemo.dpr

@@ -0,0 +1,36 @@
+program QrCodeGeneratorDemo;
+
+{$APPTYPE CONSOLE}
+{$R *.res}
+
+uses
+  SysUtils,
+  QlpArrayUtils in '..\..\QRCodeGenLib\src\Utils\QlpArrayUtils.pas',
+  QlpQRCodeGenLibTypes in '..\..\QRCodeGenLib\src\Utils\QlpQRCodeGenLibTypes.pas',
+  QlpBits in '..\..\QRCodeGenLib\src\Utils\QlpBits.pas',
+  QlpBitBuffer in '..\..\QRCodeGenLib\src\QRCodeGen\QlpBitBuffer.pas',
+  QlpQrSegment in '..\..\QRCodeGenLib\src\QRCodeGen\QlpQrSegment.pas',
+  QlpIQrSegment in '..\..\QRCodeGenLib\src\Interfaces\QlpIQrSegment.pas',
+  QlpQrSegmentMode in '..\..\QRCodeGenLib\src\QRCodeGen\QlpQrSegmentMode.pas',
+  QlpGuard in '..\..\QRCodeGenLib\src\Utils\QlpGuard.pas',
+  QlpReedSolomonGenerator in '..\..\QRCodeGenLib\src\QRCodeGen\QlpReedSolomonGenerator.pas',
+  QlpIReedSolomonGenerator in '..\..\QRCodeGenLib\src\Interfaces\QlpIReedSolomonGenerator.pas',
+  QlpQrTemplate in '..\..\QRCodeGenLib\src\QRCodeGen\QlpQrTemplate.pas',
+  QlpIQrTemplate in '..\..\QRCodeGenLib\src\Interfaces\QlpIQrTemplate.pas',
+  QlpQrCode in '..\..\QRCodeGenLib\src\QRCodeGen\QlpQrCode.pas',
+  QlpIQrCode in '..\..\QRCodeGenLib\src\Interfaces\QlpIQrCode.pas',
+  QlpQrCodeCommons in '..\..\QRCodeGenLib\src\QRCodeGen\QlpQrCodeCommons.pas',
+  QlpConverters in '..\..\QRCodeGenLib\src\Utils\QlpConverters.pas',
+  uQrCodeGeneratorDemo in '..\src\uQrCodeGeneratorDemo.pas';
+
+begin
+  try
+    { TODO -oUser -cConsole Main : Insert code here }
+    TQrCodeGeneratorDemo.RunAllDemos;
+    Readln;
+  except
+    on E: Exception do
+      Writeln(E.ClassName, ': ', E.Message);
+  end;
+
+end.

+ 69 - 0
QRCodeGenLib.Demo/FreePascal.Demo/QrCodeGeneratorDemo.lpi

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <Title Value="QrCodeGeneratorDemo"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes Count="1">
+      <Item1 Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+      <Modes Count="0"/>
+    </RunParams>
+    <RequiredPackages Count="1">
+      <Item1>
+        <PackageName Value="QRCodeGenLib4PascalPackage"/>
+      </Item1>
+    </RequiredPackages>
+    <Units Count="2">
+      <Unit0>
+        <Filename Value="QrCodeGeneratorDemo.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="..\src\uQrCodeGeneratorDemo.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit1>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <Target>
+      <Filename Value=".\bin\QrCodeGeneratorDemo"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="..\src"/>
+      <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 26 - 0
QRCodeGenLib.Demo/FreePascal.Demo/QrCodeGeneratorDemo.lpr

@@ -0,0 +1,26 @@
+program QrCodeGeneratorDemo;
+
+{$mode objfpc}{$H+}
+
+uses {$IFDEF UNIX} {$IFDEF UseCThreads}
+  cthreads, {$ENDIF} {$ENDIF}
+  SysUtils,
+  uQrCodeGeneratorDemo;
+
+begin
+  try
+    { TODO -oUser -cConsole Main : Insert code here }
+    TQrCodeGeneratorDemo.RunAllDemos;
+    Readln;
+  except
+    on E: Exception do
+      Writeln(E.ClassName, ': ', E.Message);
+  end;
+
+end.
+
+
+
+
+
+

+ 319 - 0
QRCodeGenLib.Demo/src/uQrCodeGeneratorDemo.pas

@@ -0,0 +1,319 @@
+unit uQrCodeGeneratorDemo;
+
+{$IFNDEF FPC}
+{$DEFINE DELPHI}
+{$ELSE}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+interface
+
+uses
+  SysUtils,
+  Graphics,
+{$IFDEF DELPHI}
+  Imaging.jpeg, // for Delphi JPEG Support
+{$ENDIF DELPHI}
+  QlpIQrCode,
+  QlpQrCode,
+  QlpIQrSegment,
+  QlpQrSegment,
+  QlpQrSegmentMode,
+  QlpBitBuffer,
+  QlpConverters,
+  QlpQRCodeGenLibTypes;
+
+type
+  TQrCodeGeneratorDemo = class sealed(TObject)
+  strict private
+    class function RGB(Ar, Ag, Ab: Byte): TColor; inline;
+    class function HTMLColorToTColor(const AHTMLColorHex: String)
+      : TColor; inline;
+    class procedure WriteQrCodeToFile(const AQrCode: IQrCode;
+      AScale, ABorder: Int32; const AFileName: String);
+
+    // Creates a single QR Code, then writes it to supported image formats and an SVG file.
+    class procedure DoBasicDemo();
+    // Creates a single QR Code, changes the colors (background and foreground) then writes it to supported image formats and an SVG file.
+    class procedure DoBasicDemoAndChangeColor();
+    // Creates a variety of QR Codes that exercise different features of the library, then writes each one to supported image formats and an SVG file.
+    class procedure DoVarietyDemo();
+    // Creates QR Codes with manually specified segments for better compactness, then writes each one to supported image formats and an SVG file.
+    class procedure DoSegmentDemo();
+    // Creates QR Codes with the same size and contents but different mask patterns, then writes each one to supported image formats and an SVG file.
+    class procedure DoMaskDemo();
+
+  public
+    class procedure RunAllDemos();
+  end;
+
+implementation
+
+{ TQrCodeGeneratorDemo }
+
+class function TQrCodeGeneratorDemo.RGB(Ar, Ag, Ab: Byte): TColor;
+begin
+  Result := (Ar or (Ag shl 8) or (Ab shl 16));
+end;
+
+class function TQrCodeGeneratorDemo.HTMLColorToTColor(const AHTMLColorHex
+  : String): TColor;
+var
+  Lr, Lg, Lb: Byte;
+begin
+{$IFDEF DEBUG}
+  System.Assert(System.Length(AHTMLColorHex) = 6);
+{$ENDIF DEBUG}
+  Lr := StrToInt('$' + System.Copy(AHTMLColorHex, 1, 2));
+  Lg := StrToInt('$' + System.Copy(AHTMLColorHex, 3, 2));
+  Lb := StrToInt('$' + System.Copy(AHTMLColorHex, 5, 2));
+  Result := TColor(RGB(Lr, Lg, Lb));
+end;
+
+class procedure TQrCodeGeneratorDemo.DoBasicDemo;
+var
+  LText: String;
+  LErrCorLvl: TQrCode.TEcc;
+  LQrCode: IQrCode;
+  LEncoding: TEncoding;
+begin
+  LEncoding := TEncoding.UTF8;
+  LText := 'Hello, world!'; // User-supplied Unicode text
+  LErrCorLvl := TQrCode.TEcc.eccLow; // Error correction level
+  // Make the QR Code symbol
+  LQrCode := TQrCode.EncodeText(LText, LErrCorLvl, LEncoding);
+  WriteQrCodeToFile(LQrCode, 10, 4, 'hello-world-QR');
+end;
+
+class procedure TQrCodeGeneratorDemo.DoBasicDemoAndChangeColor;
+var
+  LText: String;
+  LErrCorLvl: TQrCode.TEcc;
+  LQrCode: IQrCode;
+  LEncoding: TEncoding;
+begin
+  LEncoding := TEncoding.UTF8;
+  LText := 'Hello, world!'; // User-supplied Unicode text
+  LErrCorLvl := TQrCode.TEcc.eccLow; // Error correction level
+  // Make the QR Code symbol
+  LQrCode := TQrCode.EncodeText(LText, LErrCorLvl, LEncoding);
+  LQrCode.BackgroundColor := HTMLColorToTColor('FFA500');
+  LQrCode.ForegroundColor := HTMLColorToTColor('000000');
+  WriteQrCodeToFile(LQrCode, 10, 4, 'hello-world-orange-background-QR');
+end;
+
+class procedure TQrCodeGeneratorDemo.DoSegmentDemo;
+const
+  // Kanji mode encoding (13 bits per character)
+  kanjiChars: array [0 .. 28] of Int32 = ($0035, $1002, $0FC0, $0AED, $0AD7,
+    $015C, $0147, $0129, $0059, $01BD, $018D, $018A, $0036, $0141, $0144, $0001,
+    $0000, $0249, $0240, $0249, $0000, $0104, $0105, $0113, $0115, $0000, $0208,
+    $01FF, $0008);
+var
+  LSilver0, LSilver1, LGolden0, LGolden1, LGolden2, LMadoka: String;
+  LBitBuffer: TBitBuffer;
+  LEncoding: TEncoding;
+  LQrCode: IQrCode;
+  LSegs: TQRCodeGenLibGenericArray<IQrSegment>;
+  LCIdx: Int32;
+begin
+  LEncoding := TEncoding.UTF8;
+  // Illustration "silver"
+  LSilver0 := 'THE SQUARE ROOT OF 2 IS 1.';
+  LSilver1 :=
+    '41421356237309504880168872420969807856967187537694807317667973799';
+  LQrCode := TQrCode.EncodeText(LSilver0 + LSilver1, TQrCode.TEcc.eccLow,
+    LEncoding);
+  WriteQrCodeToFile(LQrCode, 10, 3, 'sqrt2-monolithic-QR');
+
+  LSegs := TQRCodeGenLibGenericArray<IQrSegment>.Create
+    (TQrSegment.MakeAlphanumeric(LSilver0), TQrSegment.MakeNumeric(LSilver1));
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccLow);
+  WriteQrCodeToFile(LQrCode, 10, 3, 'sqrt2-segmented-QR');
+
+  // Illustration "golden"
+  LGolden0 := 'Golden ratio φ = 1.';
+  LGolden1 :=
+    '6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374';
+  LGolden2 := '......';
+  LQrCode := TQrCode.EncodeText(LGolden0 + LGolden1 + LGolden2,
+    TQrCode.TEcc.eccLow, LEncoding);
+  WriteQrCodeToFile(LQrCode, 8, 5, 'phi-monolithic-QR.png');
+
+  LSegs := TQRCodeGenLibGenericArray<IQrSegment>.Create
+    (TQrSegment.MakeBytes(TConverters.ConvertStringToBytes(LGolden0, LEncoding)
+    ), TQrSegment.MakeNumeric(LGolden1), TQrSegment.MakeAlphanumeric(LGolden2));
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccLow);
+  WriteQrCodeToFile(LQrCode, 8, 5, 'phi-segmented-QR');
+
+  // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters
+  LMadoka := '「魔法少女まどか☆マギカ」って、 ИАИ desu κα?';
+  LQrCode := TQrCode.EncodeText(LMadoka, TQrCode.TEcc.eccLow, LEncoding);
+  WriteQrCodeToFile(LQrCode, 9, 4, 'madoka-utf8-QR');
+
+  LBitBuffer := TBitBuffer.Create();
+  for LCIdx in kanjiChars do
+  begin
+    LBitBuffer.AppendBits(LCIdx, 13);
+  end;
+  LSegs := TQRCodeGenLibGenericArray<IQrSegment>.Create
+    (TQrSegment.Create(TQrSegmentMode.qsmKanji, System.Length(kanjiChars),
+    LBitBuffer.Data, LBitBuffer.bitLength) as IQrSegment);
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccLow);
+  WriteQrCodeToFile(LQrCode, 9, 4, 'madoka-kanji-QR');
+end;
+
+class procedure TQrCodeGeneratorDemo.DoMaskDemo;
+var
+  LQrCode: IQrCode;
+  LSegs: TQRCodeGenLibGenericArray<IQrSegment>;
+  LEncoding: TEncoding;
+begin
+  LEncoding := TEncoding.UTF8;
+  // Project Nayuki URL
+  LSegs := TQrSegment.MakeSegments('https://www.nayuki.io/', LEncoding);
+  // Automatic mask
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccHigh,
+    TQrCode.MIN_VERSION, TQrCode.MAX_VERSION, -1, true);
+  WriteQrCodeToFile(LQrCode, 8, 6, 'project-nayuki-automask-QR');
+  // Force mask 3
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccHigh,
+    TQrCode.MIN_VERSION, TQrCode.MAX_VERSION, 3, true);
+  WriteQrCodeToFile(LQrCode, 8, 6, 'project-nayuki-mask3-QR');
+
+  // Chinese text as UTF-8
+  LSegs := TQrSegment.MakeSegments
+    ('維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫',
+    LEncoding);
+  // Force mask 0
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccMedium,
+    TQrCode.MIN_VERSION, TQrCode.MAX_VERSION, 0, true);
+  WriteQrCodeToFile(LQrCode, 10, 3, 'unicode-mask0-QR');
+  // Force mask 1
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccMedium,
+    TQrCode.MIN_VERSION, TQrCode.MAX_VERSION, 1, true);
+  WriteQrCodeToFile(LQrCode, 10, 3, 'unicode-mask1-QR');
+  // Force mask 5
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccMedium,
+    TQrCode.MIN_VERSION, TQrCode.MAX_VERSION, 5, true);
+  WriteQrCodeToFile(LQrCode, 10, 3, 'unicode-mask5-QR');
+  // Force mask 7
+  LQrCode := TQrCode.EncodeSegments(LSegs, TQrCode.TEcc.eccMedium,
+    TQrCode.MIN_VERSION, TQrCode.MAX_VERSION, 7, true);
+  WriteQrCodeToFile(LQrCode, 10, 3, 'unicode-mask7-QR');
+end;
+
+class procedure TQrCodeGeneratorDemo.DoVarietyDemo;
+var
+  LQrCode: IQrCode;
+  LEncoding: TEncoding;
+begin
+  LEncoding := TEncoding.UTF8;
+  // Numeric mode encoding (3.33 bits per digit)
+  LQrCode := TQrCode.EncodeText
+    ('314159265358979323846264338327950288419716939937510',
+    TQrCode.TEcc.eccMedium, LEncoding);
+  WriteQrCodeToFile(LQrCode, 13, 1, 'pi-digits-QR');
+
+  // Alphanumeric mode encoding (5.5 bits per character)
+  LQrCode := TQrCode.EncodeText
+    ('DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/',
+    TQrCode.TEcc.eccHigh, LEncoding);
+  WriteQrCodeToFile(LQrCode, 10, 2, 'alphanumeric-QR');
+
+  // Unicode text as UTF-8
+  LQrCode := TQrCode.EncodeText('こんにちwa、世界! αβγδ', TQrCode.TEcc.eccQuartile,
+    LEncoding);
+  WriteQrCodeToFile(LQrCode, 10, 3, 'unicode-QR');
+
+  // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland)
+
+  LQrCode := TQrCode.EncodeText
+    ('Alice was beginning to get very tired of sitting by her sister on the bank, '
+    + 'and of having nothing to do: once or twice she had peeped into the book her sister was reading, '
+    + 'but it had no pictures or conversations in it, ''and what is the use of a book,'' thought Alice '
+    + '''without pictures or conversations?'' So she was considering in her own mind (as well as she could, '
+    + 'for the hot day made her feel very sleepy and stupid), whether the pleasure of making a '
+    + 'daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly '
+    + 'a White Rabbit with pink eyes ran close by her.',
+    TQrCode.TEcc.eccQuartile, LEncoding);
+  WriteQrCodeToFile(LQrCode, 6, 10, 'alice-wonderland-QR');
+
+end;
+
+class procedure TQrCodeGeneratorDemo.RunAllDemos;
+begin
+  WriteLn('Started "DoBasicDemo"');
+  DoBasicDemo();
+  WriteLn('Finished "DoBasicDemo"');
+  WriteLn('Started "DoBasicDemoAndChangeColor"');
+  DoBasicDemoAndChangeColor();
+  WriteLn('Finished "DoBasicDemoAndChangeColor"');
+  WriteLn('Started "DoVarietyDemo"');
+  DoVarietyDemo();
+  WriteLn('Finished "DoVarietyDemo"');
+  WriteLn('Started "DoSegmentDemo"');
+  DoSegmentDemo();
+  WriteLn('Finished "DoSegmentDemo"');
+  WriteLn('Started "DoMaskDemo"');
+  DoMaskDemo();
+  WriteLn('Finished "DoMaskDemo"');
+
+  WriteLn(sLineBreak);
+  WriteLn('Finished Executing All Demos');
+end;
+
+class procedure TQrCodeGeneratorDemo.WriteQrCodeToFile(const AQrCode: IQrCode;
+  AScale, ABorder: Int32; const AFileName: String);
+const
+  FolderName: String = 'Assets';
+var
+  LFilePath: String;
+  LBitmap: TBitmap;
+  LJpeg: TJPEGImage;
+{$IFDEF FPC}
+  LPng: TPortableNetworkGraphic;
+{$ENDIF FPC}
+begin
+  LFilePath := ExtractFilePath(ParamStr(0));
+  LFilePath := IncludeTrailingPathDelimiter(LFilePath);
+  LFilePath := IncludeTrailingPathDelimiter(LFilePath) + FolderName;
+  LFilePath := IncludeTrailingPathDelimiter(LFilePath);
+
+  if not DirectoryExists(LFilePath) then
+  begin
+    if not ForceDirectories(LFilePath) then
+    begin
+      Exit; // break out since we cannot create our "Assets" directory.
+    end;
+  end;
+  LFilePath := LFilePath + AFileName;
+  // save bmp
+  LBitmap := AQrCode.ToBmpImage(AScale, ABorder);
+  // save jpeg
+  LJpeg := AQrCode.ToJpegImage(AScale, ABorder);
+{$IFDEF FPC}
+  // save png
+  LPng := AQrCode.ToPngImage(AScale, ABorder);
+{$ENDIF FPC}
+  try
+    try
+      LBitmap.SaveToFile(LFilePath + '.bmp');
+      LJpeg.SaveToFile(LFilePath + '.jpg');
+{$IFDEF FPC}
+      LPng.SaveToFile(LFilePath + '.png');
+{$ENDIF FPC}
+      AQrCode.ToSvgFile(ABorder, LFilePath + '.svg');
+    except
+      raise;
+    end;
+  finally
+    LBitmap.Free;
+    LJpeg.Free;
+{$IFDEF FPC}
+    LPng.Free;
+{$ENDIF FPC}
+  end;
+end;
+
+end.

+ 132 - 0
QRCodeGenLib/src/Include/QRCodeGenLib.inc

@@ -0,0 +1,132 @@
+{ *********************************************************************************** }
+{ *                              QRCodeGenLib Library                               * }
+{ *                    Copyright (c) 2018 Ugochukwu Mmaduekwe                       * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+
+ {$DEFINE DELPHI}
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+{$IFDEF FPC}
+{$I QRCodeGenLibHelper.inc} // Had to Include this Since Delphi Does not allow "FPC_FULLVERSION" to Compile.
+{$UNDEF DELPHI}
+{$MODE delphi}
+
+{$DEFINE USE_UNROLLED_VARIANT}
+
+// Disable Overflow and RangeChecks.
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+
+// Enable Pointer Math
+{$POINTERMATH ON}
+
+// Disable Warnings and Hints.
+{$WARNINGS OFF}
+{$HINTS OFF}
+{$NOTES OFF}
+
+// Optimizations
+{$OPTIMIZATION LEVEL3}
+{$OPTIMIZATION PEEPHOLE}
+{$OPTIMIZATION REGVAR}
+{$OPTIMIZATION LOOPUNROLL}
+{$OPTIMIZATION STRENGTH}
+{$OPTIMIZATION CSE}
+{$OPTIMIZATION DFA}
+{$IFDEF CPUI386}
+{$OPTIMIZATION USEEBP}
+{$ENDIF}
+{$IFDEF CPUX86_64}
+{$OPTIMIZATION USERBP}
+{$ENDIF}
+{$ENDIF FPC}
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+{$IFDEF DELPHI}
+
+{$DEFINE USE_UNROLLED_VARIANT}
+
+// This option is needed to enable code browsing (aka Ctrl+Click)
+// It does not affect the binary size or generated code
+{$DEFINITIONINFO ON}
+
+// Disable Overflow and RangeChecks.
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+
+ // Enable Pointer Math
+{$POINTERMATH ON}
+
+// Disable String Checks
+{$STRINGCHECKS OFF}
+
+// Disable Duplicate Constructor Warnings
+{$WARN DUPLICATE_CTOR_DTOR OFF}
+
+ // 2010 only
+{$IF CompilerVersion = 21.0}
+{$DEFINE DELPHI2010}
+{$IFEND}
+
+  // 2010 and Above
+{$IF CompilerVersion >= 21.0}
+{$DEFINE DELPHI2010_UP}
+{$IFEND}
+
+  // XE and Above
+{$IF CompilerVersion >= 22.0}
+{$DEFINE DELPHIXE_UP}
+{$IFEND}
+
+  // XE2 and Above
+{$IF CompilerVersion >= 23.0}
+{$DEFINE DELPHIXE2_UP}
+{$DEFINE HAS_UNITSCOPE}
+{$IFEND}
+
+// XE3 and Below
+{$IF CompilerVersion <= 24.0}
+{$DEFINE DELPHIXE3_DOWN}
+{$IFEND}
+
+ // XE3 and Above
+{$IF CompilerVersion >= 24.0}
+{$DEFINE DELPHIXE3_UP}
+{$LEGACYIFEND ON}
+{$ZEROBASEDSTRINGS OFF}
+{$IFEND}
+
+  // XE7 and Above
+{$IF CompilerVersion >= 28.0}
+{$DEFINE DELPHIXE7_UP}
+{$IFEND}
+
+  // 10.2 Tokyo and Above
+{$IF CompilerVersion >= 32.0}
+{$DEFINE DELPHI10.2_TOKYO_UP}
+{$IFEND}
+
+  // 2010 and Above
+{$IFNDEF DELPHI2010_UP}
+{$MESSAGE ERROR 'This Library requires Delphi 2010 or higher.'}
+{$ENDIF}
+
+  // 10.2 Tokyo and Above
+{$IFDEF DELPHI10.2_TOKYO_UP}
+{$WARN COMBINING_SIGNED_UNSIGNED64 OFF}
+{$ENDIF}
+
+
+{$ENDIF DELPHI}
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+

+ 22 - 0
QRCodeGenLib/src/Include/QRCodeGenLibHelper.inc

@@ -0,0 +1,22 @@
+{ *********************************************************************************** }
+{ *                              QRCodeGenLib Library                               * }
+{ *                    Copyright (c) 2018 Ugochukwu Mmaduekwe                       * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+
+{$MACRO ON}
+{$IFDEF ENDIAN_BIG}
+{$MESSAGE FATAL 'This Library does not support "Big Endian" processors yet.'}
+{$ENDIF}
+// FPC 3.0.0 and Above
+// Had to Include this here since Delphi does not allow it Compile in "QRCodeGenLib.inc".
+{$IF FPC_FULLVERSION < 30000}
+{$MESSAGE ERROR 'This Library requires FreePascal 3.0.0 or higher.'}
+{$IFEND}

+ 178 - 0
QRCodeGenLib/src/Interfaces/QlpIQrCode.pas

@@ -0,0 +1,178 @@
+unit QlpIQrCode;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  Graphics,
+{$IFDEF DELPHI}
+  Imaging.jpeg, // for Delphi JPEG Support
+{$ENDIF DELPHI}
+  QlpQRCodeGenLibTypes;
+
+type
+  IQrCode = interface(IInterface)
+    ['{BD801EA2-0A27-46B0-B1CB-0AD8A3EC71AE}']
+
+    function GetVersion: Int32;
+    property Version: Int32 read GetVersion;
+    function GetSize: Int32;
+    property Size: Int32 read GetSize;
+    function GetMask: Int32;
+    property Mask: Int32 read GetMask;
+    function GetModules: TQRCodeGenLibInt32Array;
+    property Modules: TQRCodeGenLibInt32Array read GetModules;
+    function GetBackgroundColor: TColor;
+    procedure SetBackgroundColor(const AColor: TColor);
+    property BackgroundColor: TColor read GetBackgroundColor
+      write SetBackgroundColor;
+    function GetForegroundColor: TColor;
+    procedure SetForegroundColor(const AColor: TColor);
+    property ForegroundColor: TColor read GetForegroundColor
+      write SetForegroundColor;
+
+    /// <summary>
+    /// Returns the color of the module (pixel) at the specified coordinates,
+    /// which is either false for white or true for black. The top left
+    /// corner has the coordinates (x=0, y=0).If the specified coordinates
+    /// are out of bounds, then false (white) is returned.
+    /// </summary>
+    /// <param name="Ax">
+    /// the x coordinate, where 0 is the left edge and FSize - 1 is the right
+    /// edge
+    /// </param>
+    /// <param name="Ay">
+    /// the y coordinate, where 0 is the top edge and FSize - 1 is the bottom
+    /// edge
+    /// </param>
+    /// <returns>
+    /// the module's color, which is either false (white) or true (black)
+    /// </returns>
+    function GetModule(Ax, Ay: Int32): Boolean;
+
+    /// <summary>
+    /// Returns a bitmap image depicting this QR Code, with the specified
+    /// module scale and border modules. For example, ToBmpImage(scale=10,
+    /// border=4) means to pad the QR Code with 4 white border modules on all
+    /// four sides, and use 10×10 pixels to represent each module. The
+    /// resulting image contains the colors specified by the backgroundcolor
+    /// (by default = FFFFFF) and foregroundcolor (by default = 000000)
+    /// properties.
+    /// </summary>
+    /// <param name="AScale">
+    /// the side length (measured in pixels, must be positive) of each module
+    /// </param>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a new bmp image representing this QR Code, with padding and scaling
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the scale or border is out of range, or if <br />{AScale, ABorder,
+    /// FSize} cause the image dimensions to exceed System.High(Int32)
+    /// </exception>
+    /// <remarks>
+    /// <b>The caller is responsible for the lifetime of the returned image
+    /// object.</b>
+    /// </remarks>
+    function ToBmpImage(AScale, ABorder: Int32): TBitmap;
+
+    /// <summary>
+    /// Returns a jpeg image depicting this QR Code, with the specified
+    /// module scale and border modules. For example, ToBmpImage(scale=10,
+    /// border=4) means to pad the QR Code with 4 white border modules on all
+    /// four sides, and use 10×10 pixels to represent each module. The
+    /// resulting image contains the colors specified by the backgroundcolor
+    /// (by default = FFFFFF) and foregroundcolor (by default = 000000)
+    /// properties.
+    /// </summary>
+    /// <param name="AScale">
+    /// the side length (measured in pixels, must be positive) of each module
+    /// </param>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a new jpeg image representing this QR Code, with padding and scaling
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the scale or border is out of range, or if <br />{AScale, ABorder,
+    /// FSize} cause the image dimensions to exceed System.High(Int32)
+    /// </exception>
+    /// <remarks>
+    /// <b>The caller is responsible for the lifetime of the returned image
+    /// object.</b>
+    /// </remarks>
+    function ToJpegImage(AScale, ABorder: Int32): TJPEGImage;
+
+{$IFDEF FPC}
+    /// <summary>
+    /// Returns a png image depicting this QR Code, with the specified
+    /// module scale and border modules. For example, ToBmpImage(scale=10,
+    /// border=4) means to pad the QR Code with 4 white border modules on all
+    /// four sides, and use 10×10 pixels to represent each module. The
+    /// resulting image contains the colors specified by the backgroundcolor
+    /// (by default = FFFFFF) and foregroundcolor (by default = 000000)
+    /// properties.
+    /// </summary>
+    /// <param name="AScale">
+    /// the side length (measured in pixels, must be positive) of each module
+    /// </param>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a new png image representing this QR Code, with padding and scaling
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the scale or border is out of range, or if <br />{AScale, ABorder,
+    /// FSize} cause the image dimensions to exceed System.High(Int32)
+    /// </exception>
+    /// <remarks>
+    /// <b>The caller is responsible for the lifetime of the returned image
+    /// object.</b>
+    /// </remarks>
+    function ToPngImage(AScale, ABorder: Int32): TPortableNetworkGraphic;
+{$ENDIF FPC}
+    /// <summary>
+    /// Returns a string of SVG code for an image depicting this QR Code,
+    /// with the specified number of border modules. The string always uses
+    /// Unix newlines AnsiChar(#10), regardless of the platform.
+    /// </summary>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a string representing this QR Code as an SVG XML document
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the border is negative
+    /// </exception>
+    function ToSvgString(ABorder: Int32): String;
+
+    /// <summary>
+    /// saves a string of SVG code for an image depicting this QR Code, with
+    /// the specified number of border modules as an svg file. The string
+    /// always uses Unix newlines AnsiChar(#10), regardless of the platform.
+    /// </summary>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <param name="AFileName">
+    /// the filename to save the output to <b>with file extension</b>
+    /// </param>
+    /// <returns>
+    /// true on success and false on failure.
+    /// </returns>
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the border is negative
+    /// </exception>
+    function ToSvgFile(ABorder: Int32; const AFileName: String): Boolean;
+  end;
+
+implementation
+
+end.

+ 28 - 0
QRCodeGenLib/src/Interfaces/QlpIQrSegment.pas

@@ -0,0 +1,28 @@
+unit QlpIQrSegment;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  QlpQrSegmentMode,
+  QlpQRCodeGenLibTypes;
+
+type
+  IQrSegment = interface(IInterface)
+    ['{2447C529-F7E7-4E6C-B21F-81E2455DA8B6}']
+
+    function GetMode: TQrSegmentMode;
+    property Mode: TQrSegmentMode read GetMode;
+    function GetNumChars: Int32;
+    property NumChars: Int32 read GetNumChars;
+    function GetBitLength: Int32;
+    property BitLength: Int32 read GetBitLength;
+    function GetData: TQRCodeGenLibInt32Array;
+    property Data: TQRCodeGenLibInt32Array read GetData;
+
+  end;
+
+implementation
+
+end.

+ 26 - 0
QRCodeGenLib/src/Interfaces/QlpIQrTemplate.pas

@@ -0,0 +1,26 @@
+unit QlpIQrTemplate;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  QlpQRCodeGenLibTypes;
+
+type
+  IQrTemplate = interface(IInterface)
+    ['{BFD8DF9F-5A1E-45ED-B928-388604BF3112}']
+
+    function GetTemplate(): TQRCodeGenLibInt32Array;
+    property Template: TQRCodeGenLibInt32Array read GetTemplate;
+    function GetDataOutputBitIndexes(): TQRCodeGenLibInt32Array;
+    property DataOutputBitIndexes: TQRCodeGenLibInt32Array
+      read GetDataOutputBitIndexes;
+    function GetMasks(): TQRCodeGenLibMatrixInt32Array;
+    property Masks: TQRCodeGenLibMatrixInt32Array read GetMasks;
+
+  end;
+
+implementation
+
+end.

+ 20 - 0
QRCodeGenLib/src/Interfaces/QlpIReedSolomonGenerator.pas

@@ -0,0 +1,20 @@
+unit QlpIReedSolomonGenerator;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  QlpQRCodeGenLibTypes;
+
+type
+  IReedSolomonGenerator = interface(IInterface)
+    ['{589D6EEB-8B78-478C-A316-F9FB02BDFAF2}']
+
+    procedure GetRemainder(const AData: TQRCodeGenLibByteArray;
+      ADataOff, ADataLen: Int32; const AResult: TQRCodeGenLibByteArray);
+  end;
+
+implementation
+
+end.

+ 116 - 0
QRCodeGenLib/src/Packages/FPC/QRCodeGenLib4PascalPackage.lpk

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <Package Version="4">
+    <PathDelim Value="\"/>
+    <Name Value="QRCodeGenLib4PascalPackage"/>
+    <Type Value="RunTimeOnly"/>
+    <Author Value="Xor-el (Ugochukwu Mmaduekwe)"/>
+    <CompilerOptions>
+      <Version Value="11"/>
+      <PathDelim Value="\"/>
+      <SearchPaths>
+        <IncludeFiles Value="..\..\Include"/>
+        <OtherUnitFiles Value="..\..\Interfaces;..\..\Utils;..\..\QRCodeGen"/>
+        <UnitOutputDirectory Value="lib\QRCodeGenLib4Pascal\$(TargetCPU)-$(TargetOS)"/>
+      </SearchPaths>
+      <CodeGeneration>
+        <Optimizations>
+          <OptimizationLevel Value="4"/>
+        </Optimizations>
+      </CodeGeneration>
+    </CompilerOptions>
+    <Description Value="QRCodeGenLib4Pascal is a Delphi/FPC compatible library that provides an easy to use interface for generating QR Codes.
+"/>
+    <License Value="MIT License"/>
+    <Version Major="1"/>
+    <Files Count="18">
+      <Item1>
+        <Filename Value="..\..\QRCodeGen\QlpBitBuffer.pas"/>
+        <UnitName Value="QlpBitBuffer"/>
+      </Item1>
+      <Item2>
+        <Filename Value="..\..\QRCodeGen\QlpQrCode.pas"/>
+        <UnitName Value="QlpQrCode"/>
+      </Item2>
+      <Item3>
+        <Filename Value="..\..\QRCodeGen\QlpQrCodeCommons.pas"/>
+        <UnitName Value="QlpQrCodeCommons"/>
+      </Item3>
+      <Item4>
+        <Filename Value="..\..\QRCodeGen\QlpQrSegment.pas"/>
+        <UnitName Value="QlpQrSegment"/>
+      </Item4>
+      <Item5>
+        <Filename Value="..\..\QRCodeGen\QlpQrSegmentMode.pas"/>
+        <UnitName Value="QlpQrSegmentMode"/>
+      </Item5>
+      <Item6>
+        <Filename Value="..\..\QRCodeGen\QlpQrTemplate.pas"/>
+        <UnitName Value="QlpQrTemplate"/>
+      </Item6>
+      <Item7>
+        <Filename Value="..\..\QRCodeGen\QlpReedSolomonGenerator.pas"/>
+        <UnitName Value="QlpReedSolomonGenerator"/>
+      </Item7>
+      <Item8>
+        <Filename Value="..\..\Interfaces\QlpIQrCode.pas"/>
+        <UnitName Value="QlpIQrCode"/>
+      </Item8>
+      <Item9>
+        <Filename Value="..\..\Interfaces\QlpIQrSegment.pas"/>
+        <UnitName Value="QlpIQrSegment"/>
+      </Item9>
+      <Item10>
+        <Filename Value="..\..\Interfaces\QlpIQrTemplate.pas"/>
+        <UnitName Value="QlpIQrTemplate"/>
+      </Item10>
+      <Item11>
+        <Filename Value="..\..\Interfaces\QlpIReedSolomonGenerator.pas"/>
+        <UnitName Value="QlpIReedSolomonGenerator"/>
+      </Item11>
+      <Item12>
+        <Filename Value="..\..\Include\QRCodeGenLib.inc"/>
+        <Type Value="Include"/>
+      </Item12>
+      <Item13>
+        <Filename Value="..\..\Include\QRCodeGenLibHelper.inc"/>
+        <Type Value="Include"/>
+      </Item13>
+      <Item14>
+        <Filename Value="..\..\Utils\QlpArrayUtils.pas"/>
+        <UnitName Value="QlpArrayUtils"/>
+      </Item14>
+      <Item15>
+        <Filename Value="..\..\Utils\QlpBits.pas"/>
+        <UnitName Value="QlpBits"/>
+      </Item15>
+      <Item16>
+        <Filename Value="..\..\Utils\QlpGuard.pas"/>
+        <UnitName Value="QlpGuard"/>
+      </Item16>
+      <Item17>
+        <Filename Value="..\..\Utils\QlpQRCodeGenLibTypes.pas"/>
+        <UnitName Value="QlpQRCodeGenLibTypes"/>
+      </Item17>
+      <Item18>
+        <Filename Value="..\..\Utils\QlpConverters.pas"/>
+        <UnitName Value="QlpConverters"/>
+      </Item18>
+    </Files>
+    <LazDoc PackageName="(default)"/>
+    <RequiredPkgs Count="2">
+      <Item1>
+        <PackageName Value="LCL"/>
+      </Item1>
+      <Item2>
+        <PackageName Value="FCL"/>
+      </Item2>
+    </RequiredPkgs>
+    <UsageOptions>
+      <UnitPath Value="$(PkgOutDir)"/>
+    </UsageOptions>
+    <PublishOptions>
+      <Version Value="2"/>
+    </PublishOptions>
+  </Package>
+</CONFIG>

+ 18 - 0
QRCodeGenLib/src/Packages/FPC/QRCodeGenLib4PascalPackage.pas

@@ -0,0 +1,18 @@
+{ This file was automatically created by Lazarus. Do not edit!
+  This source is only used to compile and install the package.
+ }
+
+unit QRCodeGenLib4PascalPackage;
+
+{$warn 5023 off : no warning about unused units}
+interface
+
+uses
+  QlpBitBuffer, QlpQrCode, QlpQrCodeCommons, QlpQrSegment, QlpQrSegmentMode, 
+  QlpQrTemplate, QlpReedSolomonGenerator, QlpIQrCode, QlpIQrSegment, 
+  QlpIQrTemplate, QlpIReedSolomonGenerator, QlpArrayUtils, QlpBits, QlpGuard, 
+  QlpQRCodeGenLibTypes, QlpConverters;
+
+implementation
+
+end.

+ 257 - 0
QRCodeGenLib/src/QRCodeGen/QlpBitBuffer.pas

@@ -0,0 +1,257 @@
+unit QlpBitBuffer;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  QlpBits,
+  QlpArrayUtils,
+  QlpQRCodeGenLibTypes;
+
+resourcestring
+  SInvalidDataContent = 'Data is not a whole number of bytes';
+  SValueOutOfRange = 'Value out of range';
+  SLastWordError = 'Last word must have low bits clear';
+  SMaximumLengthReached = 'Maximum length reached';
+  SBitBufferNotInitialized = 'BitBuffer not initialized';
+
+type
+
+  /// <summary>
+  /// An appendable sequence of bits (0s and 1s). Mainly used by <see cref="QlpIQrSegment">
+  /// TQrSegment</see>.
+  /// </summary>
+  TBitBuffer = record
+  strict private
+  var
+    FData: TQRCodeGenLibInt32Array;
+    FBitLength: Int32;
+    FBitBufferInitialized: Boolean;
+
+    function GetData: TQRCodeGenLibInt32Array; inline;
+    function GetBitLength: Int32; inline;
+
+    /// <summary>
+    /// method to check if <c>BitBuffer</c> record is initialized or not.
+    /// </summary>
+    /// <exception cref="EInvalidOperationQRCodeGenLibException">
+    /// if <c>BitBuffer</c> is not initialized.
+    /// </exception>
+    procedure CheckBitBufferState(); inline;
+
+  public
+
+    /// <summary>
+    /// Constructs an empty bit buffer (length 0).
+    /// </summary>
+    class function Create(): TBitBuffer; static;
+
+    /// <summary>
+    /// Returns the length of this sequence, which is a non-negative value.
+    /// </summary>
+    /// <returns>
+    /// the length of this sequence
+    /// </returns>
+    function GetBit(AIndex: Int32): Int32; inline;
+
+    /// <summary>
+    /// Returns an array representing this buffer's bits packed into bytes in
+    /// big endian. The current bit length must be a multiple of 8.
+    /// </summary>
+    /// <returns>
+    /// a new byte array representing this bit sequence
+    /// </returns>
+    function GetBytes(): TQRCodeGenLibByteArray; inline;
+
+    /// <summary>
+    /// Appends the specified number of low-order bits of the specified value
+    /// to this buffer. Requires 0 ≤ len ≤ 31 and 0 ≤ val &lt; 2 <sup>len</sup>
+    /// .
+    /// </summary>
+    /// <param name="AValue">
+    /// the value to append
+    /// </param>
+    /// <param name="ALength">
+    /// the number of low-order bits in the value to take
+    /// </param>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the value or number of bits is out of range
+    /// </exception>
+    /// <exception cref="QlpQRCodeGenLibTypes|EInvalidOperationQRCodeGenLibException">
+    /// if appending the data would make <c>BitLength</c> exceed
+    /// System.High(Int32)
+    /// </exception>
+    procedure AppendBits(AValue, ALength: Int32); overload;
+
+    procedure AppendBits(const AValues: TQRCodeGenLibInt32Array;
+      ALength: Int32); overload;
+
+    property Data: TQRCodeGenLibInt32Array read GetData;
+    property BitLength: Int32 read GetBitLength;
+
+  end;
+
+implementation
+
+{ TBitBuffer }
+
+procedure TBitBuffer.CheckBitBufferState;
+begin
+  if not FBitBufferInitialized then
+  begin
+    raise EInvalidOperationQRCodeGenLibException.CreateRes
+      (@SBitBufferNotInitialized);
+  end;
+end;
+
+class function TBitBuffer.Create(): TBitBuffer;
+begin
+  Result := Default(TBitBuffer);
+  System.SetLength(Result.FData, 64);
+  Result.FBitLength := 0;
+  Result.FBitBufferInitialized := True;
+end;
+
+procedure TBitBuffer.AppendBits(AValue, ALength: Int32);
+var
+  LRemain: Int32;
+begin
+  CheckBitBufferState();
+  if ((ALength < 0) or (ALength > 31) or (TBits.Asr32(AValue, ALength) <> 0))
+  then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes(@SValueOutOfRange);
+  end;
+
+  if (ALength > (System.High(Int32) - FBitLength)) then
+  begin
+    raise EInvalidOperationQRCodeGenLibException.CreateRes
+      (@SMaximumLengthReached);
+  end;
+
+  if ((FBitLength + ALength + 1) > (System.Length(FData) shl 5)) then
+  begin
+    FData := TArrayUtils.CopyOf(FData, System.Length(FData) * 2);
+  end;
+{$IFDEF DEBUG}
+  System.Assert((FBitLength + ALength) <= System.Length(FData) shl 5);
+{$ENDIF DEBUG}
+  LRemain := 32 - (FBitLength and $1F);
+{$IFDEF DEBUG}
+  System.Assert((1 <= LRemain) and (LRemain <= 32));
+{$ENDIF DEBUG}
+  if (LRemain < ALength) then
+  begin
+    FData[TBits.Asr32(FBitLength, 5)] := FData[TBits.Asr32(FBitLength, 5)] or
+      TBits.Asr32(AValue, (ALength - LRemain));
+    FBitLength := FBitLength + LRemain;
+{$IFDEF DEBUG}
+    System.Assert((FBitLength and $1F) = 0);
+{$ENDIF DEBUG}
+    ALength := ALength - LRemain;
+    AValue := AValue and ((1 shl ALength) - 1);
+    LRemain := 32;
+  end;
+  FData[TBits.Asr32(FBitLength, 5)] := FData[TBits.Asr32(FBitLength, 5)] or
+    (AValue shl (LRemain - ALength));
+  FBitLength := FBitLength + ALength;
+end;
+
+procedure TBitBuffer.AppendBits(const AValues: TQRCodeGenLibInt32Array;
+  ALength: Int32);
+var
+  LWholeWords, LTailBits, LShift, LWord, LIdx: Int32;
+begin
+  CheckBitBufferState();
+  if (ALength = 0) then
+  begin
+    Exit;
+  end;
+  if ((ALength < 0) or (ALength > System.Length(AValues) * Int64(32))) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes(@SValueOutOfRange);
+  end;
+  LWholeWords := ALength shr 5;
+  LTailBits := ALength and 31;
+  if ((LTailBits > 0) and ((AValues[LWholeWords] shl LTailBits) <> 0)) then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.CreateRes(@SLastWordError);
+  end;
+
+  if (ALength > (System.High(Int32) - FBitLength)) then
+  begin
+    raise EInvalidOperationQRCodeGenLibException.CreateRes
+      (@SMaximumLengthReached);
+  end;
+
+  while ((FBitLength + ALength) > (System.Length(FData) * 32)) do
+  begin
+    FData := TArrayUtils.CopyOf(FData, System.Length(FData) * 2);
+  end;
+
+  LShift := FBitLength and 31;
+  if (LShift = 0) then
+  begin
+    System.Move(AValues[0], FData[FBitLength shr 5], ((ALength + 31) shr 5) *
+      System.SizeOf(Int32));
+    FBitLength := FBitLength + ALength;
+  end
+  else
+  begin
+    for LIdx := 0 to System.Pred(LWholeWords) do
+    begin
+      LWord := AValues[LIdx];
+      FData[TBits.Asr32(FBitLength, 5)] := FData[TBits.Asr32(FBitLength, 5)] or
+        (LWord shr LShift);
+      FBitLength := FBitLength + 32;
+      FData[TBits.Asr32(FBitLength, 5)] := LWord shl (32 - LShift);
+    end;
+    if (LTailBits > 0) then
+    begin
+      AppendBits(AValues[LWholeWords] shr (32 - LTailBits), LTailBits);
+    end;
+  end;
+end;
+
+function TBitBuffer.GetBit(AIndex: Int32): Int32;
+begin
+  CheckBitBufferState();
+  if ((AIndex < 0) or (AIndex >= FBitLength)) then
+  begin
+    raise EIndexOutOfRangeQRCodeGenLibException.Create('');
+  end;
+  Result := (TBits.Asr32(FData[TBits.Asr32(AIndex, 5)], not(AIndex))) and 1;
+end;
+
+function TBitBuffer.GetBitLength: Int32;
+begin
+  CheckBitBufferState();
+  Result := FBitLength;
+end;
+
+function TBitBuffer.GetBytes: TQRCodeGenLibByteArray;
+var
+  LIdx: Int32;
+begin
+  CheckBitBufferState();
+  if ((FBitLength and 7) <> 0) then
+  begin
+    raise EInvalidOperationQRCodeGenLibException.CreateRes
+      (@SInvalidDataContent);
+  end;
+  System.SetLength(Result, FBitLength shr 3);
+  for LIdx := 0 to System.Pred(System.Length(Result)) do
+  begin
+    Result[LIdx] := Byte(TBits.Asr32(FData[TBits.Asr32(LIdx, 2)],
+      ((not LIdx) shl 3)));
+  end;
+end;
+
+function TBitBuffer.GetData: TQRCodeGenLibInt32Array;
+begin
+  CheckBitBufferState();
+  Result := System.Copy(FData);
+end;
+
+end.

+ 1317 - 0
QRCodeGenLib/src/QRCodeGen/QlpQrCode.pas

@@ -0,0 +1,1317 @@
+unit QlpQrCode;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  Math,
+  Classes,
+  SysUtils,
+  Graphics,
+{$IFDEF DELPHI}
+  Imaging.jpeg, // for Delphi JPEG Support
+{$ELSE}
+  Interfaces, // Added so that Lazarus/FPC will Initialize the WidgetSet
+{$ENDIF DELPHI}
+  QlpIQrCode,
+  QlpIQrTemplate,
+  QlpQrTemplate,
+  QlpIQrSegment,
+  QlpQrSegment,
+  QlpQrSegmentMode,
+  QlpBitBuffer,
+  QlpIReedSolomonGenerator,
+  QlpReedSolomonGenerator,
+  QlpQrCodeCommons,
+  QlpGuard,
+  QlpBits,
+  QlpConverters,
+  QlpQRCodeGenLibTypes;
+
+resourcestring
+  SValueOutOfRange = 'Value out of range';
+  SScaleOrBorderTooLarge = 'Scale or border too large';
+  SMaskOutOfRange = 'Mask out of range';
+  SInvalidState = 'Invalid state encountered.';
+  SInvalidValue = 'Invalid value';
+  SDataTooLong = 'Data too long';
+  SFileNameEmpty = 'FileName cannot be empty';
+  SBorderNegative = 'Border must be non-negative';
+  SEncodingInstanceNil = 'Encoding instance cannot be nil';
+
+type
+
+  /// <summary>
+  /// A QR Code symbol, which is a type of two-dimension barcode. Invented by
+  /// Denso Wave and described in the ISO/IEC 18004 standard. Instances of
+  /// this class represent an immutable square grid of black (default) and white (default) cells.
+  /// The class provides static factory functions to create a QR Code from
+  /// text or binary data. The class covers the QR Code Model 2
+  /// specification, supporting all versions (sizes) from 1 to 40, all 4
+  /// error correction levels, and 4 character encoding modes.
+  /// </summary>
+  TQrCode = class sealed(TInterfacedObject, IQrCode)
+
+  public
+
+    type
+{$SCOPEDENUMS ON}
+    /// <summary>
+    /// The error correction level in a QR Code symbol.
+    /// </summary>
+    TEcc = (
+      /// <summary>
+      /// The QR Code can tolerate about 7% erroneous codewords.
+      /// </summary>
+      eccLow = 1,
+      /// <summary>
+      /// The QR Code can tolerate about 15% erroneous codewords.
+      /// </summary>
+      eccMedium = 0,
+      /// <summary>
+      /// The QR Code can tolerate about 25% erroneous codewords.
+      /// </summary>
+      eccQuartile = 3,
+      /// <summary>
+      /// The QR Code can tolerate about 30% erroneous codewords.
+      /// </summary>
+      eccHigh = 2
+
+      );
+{$SCOPEDENUMS OFF}
+  strict private
+  const
+    UNIX_NEW_LINE = AnsiString(#10);
+    TAB = AnsiString(#9);
+
+    // For use in GetPenaltyScore(), when evaluating which mask is best.
+    PENALTY_N1 = Int32(3);
+    PENALTY_N2 = Int32(3);
+    PENALTY_N3 = Int32(40);
+    PENALTY_N4 = Int32(10);
+
+    /// <summary>
+    /// helper static constant array for implementing <c>for .. in</c> loops
+    /// for <c>TEcc</c> Enums. This was implemented because I needed to retain
+    /// the order the enum was defined while looping through it.
+    /// </summary>
+    EccOrder: array [0 .. 3] of TEcc = (TEcc.eccLow, TEcc.eccMedium,
+      TEcc.eccQuartile, TEcc.eccHigh);
+
+ ECC_CODEWORDS_PER_BLOCK : array[0 .. 3, 0 .. 40] of Int8 = (
+	// Version: (note that index 0 is for padding, and is set to an illegal value)
+	//0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
+	(-1,  7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30),  // Low
+	(-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28),  // Medium
+	(-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30),  // Quartile
+	(-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30)  // High
+);
+
+ NUM_ERROR_CORRECTION_BLOCKS : array[0 .. 3, 0 .. 40] of Int8 = (
+	// Version: (note that index 0 is for padding, and is set to an illegal value)
+	//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
+	(-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4,  4,  4,  4,  4,  6,  6,  6,  6,  7,  8,  8,  9,  9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25),  // Low
+	(-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5,  5,  8,  9,  9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49),  // Medium
+	(-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8,  8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68),  // Quartile
+	(-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81)  // High
+);
+
+  type
+    TEccHelper = record helper for TEcc
+    strict private
+      function GetFormatBits(): Int32; inline;
+      function GetToInt32(): Int32;
+
+    public
+      property FormatBits: Int32 read GetFormatBits;
+      property ToInt32: Int32 read GetToInt32;
+    end;
+
+  var
+    FVersion, FSize, FMask: Int32;
+
+    /// <summary>
+    /// The error correction level used in this QR Code
+    /// </summary>
+    FErrorCorrectionLevel: TEcc;
+    FModules: TQRCodeGenLibInt32Array;
+    FBackgroundColor, FForegroundColor: TColor;
+
+    function GetVersion: Int32; inline;
+    function GetSize: Int32; inline;
+    function GetMask: Int32; inline;
+    function GetModules: TQRCodeGenLibInt32Array; inline;
+    function GetBackgroundColor: TColor; inline;
+    function GetForegroundColor: TColor; inline;
+    procedure SetBackgroundColor(const AColor: TColor); inline;
+    procedure SetForegroundColor(const AColor: TColor); inline;
+
+    // Draws two copies of the format bits (with its own error correction code)
+    // based on the given mask and this object's error correction level field.
+    procedure DrawFormatBits(AMask: Int32);
+
+    /// <summary>
+    /// Sets the module at the given coordinates to the given color. <br />
+    /// Only used by the constructor. Coordinates must be in bounds.
+    /// </summary>
+    procedure SetModule(Ax, Ay, ABlack: Int32);
+
+    // Returns a new byte string representing the given data with the appropriate error correction
+    // codewords appended to it, based on this object's version and error correction level.
+    function AddEccAndInterleave(const AData: TQRCodeGenLibByteArray)
+      : TQRCodeGenLibByteArray;
+
+    // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
+    // data area of this QR Code symbol. Function modules need to be marked off before this is called.
+    procedure DrawCodeWords(const ADataOutputBitIndexes
+      : TQRCodeGenLibInt32Array; const AAllCodewords: TQRCodeGenLibByteArray);
+
+    // XORs the codeword modules in this QR Code with the given mask pattern.
+    // The function modules must be marked and the codeword bits must be drawn
+    // before masking. Due to the arithmetic of XOR, calling ApplyMask() with
+    // the same mask value a second time will undo the mask. A final well-formed
+    // QR Code symbol needs exactly one (not zero, two, etc.) mask applied.
+    procedure ApplyMask(const AMask: TQRCodeGenLibInt32Array);
+
+    // A messy helper function for the constructor. This QR Code must be in an unmasked state when this
+    // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed.
+    // This method applies and returns the actual mask chosen, from 0 to 7.
+    function HandleConstructorMasking(const AMasks
+      : TQRCodeGenLibMatrixInt32Array; AMask: Int32): Int32;
+
+    // Calculates and returns the penalty score based on state of this QR Code's current modules.
+    // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
+    function GetPenaltyScore(): Int32;
+
+    procedure ValidateImageDimensions(AScale, ABorder: Int32);
+
+    // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
+    // QR Code of the given version number and error correction level, with remainder bits discarded.
+    // This stateless pure function could be implemented as a (40*4)-cell lookup table.
+    class function GetNumDataCodeWords(AVersion: Int32; AEcl: TEcc)
+      : Int32; inline;
+
+    class function GetRValue(Argb: UInt32): Byte; static; inline;
+    class function GetGValue(Argb: UInt32): Byte; static; inline;
+    class function GetBValue(Argb: UInt32): Byte; static; inline;
+
+    /// <summary>
+    /// Convert a Delphi/Lazarus <c>TColor</c> to <c>HTML</c> Color code in
+    /// Hex <c>.</c>
+    /// </summary>
+    /// <param name="AColor">
+    /// the <c>TColor</c> to convert
+    /// </param>
+    /// <returns>
+    /// returns a string containing the <c>HTML</c> Color code representation
+    /// of the <c>TColor</c> parameter in Hex
+    /// </returns>
+    class function TColorToHTMLColorHex(const AColor: TColor): String; inline;
+
+  public
+
+    const
+    MIN_VERSION = TQrCodeCommons.MIN_VERSION;
+    MAX_VERSION = TQrCodeCommons.MAX_VERSION;
+
+    /// <summary>
+    /// Returns the color of the module (pixel) at the specified coordinates,
+    /// which is either <c>false</c> for white or <c>true</c> for black. The
+    /// top left corner has the coordinates (x=0, y=0).If the specified
+    /// coordinates are out of bounds, then <c>false</c> (white) is returned.
+    /// </summary>
+    /// <param name="Ax">
+    /// the x coordinate, where 0 is the left edge and FSize - 1 is the right
+    /// edge
+    /// </param>
+    /// <param name="Ay">
+    /// the y coordinate, where 0 is the top edge and FSize - 1 is the bottom
+    /// edge
+    /// </param>
+    /// <returns>
+    /// the module's color, which is either <c>false</c> (white) or <c>true</c>
+    /// (black)
+    /// </returns>
+    function GetModule(Ax, Ay: Int32): Boolean; inline;
+
+    /// <summary>
+    /// Constructs a QR Code with the specified version number, error
+    /// correction level, data codeword bytes, and mask number.
+    /// </summary>
+    /// <param name="AVersion">
+    /// the version number to use, which must be in the range 1 to 40
+    /// (inclusive)
+    /// </param>
+    /// <param name="AEcl">
+    /// the error correction level to use
+    /// </param>
+    /// <param name="ADataCodewords">
+    /// the bytes representing segments to encode (without ECC)
+    /// </param>
+    /// <param name="AMask">
+    /// the mask pattern to use, which is either −1 for automatic choice or
+    /// from 0 to 7 for fixed choice
+    /// </param>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the version or mask value is out of range, <br />or if the data is
+    /// the wrong length for the specified version and error correction level
+    /// </exception>
+    constructor Create(AVersion: Int32; AEcl: TEcc;
+      const ADataCodewords: TQRCodeGenLibByteArray; AMask: Int32);
+
+    /// <summary>
+    /// The version number of this QR Code, which is between 1 and 40
+    /// (inclusive). <br />This determines the size of this barcode.
+    /// </summary>
+    property Version: Int32 read GetVersion;
+
+    /// <summary>
+    /// The width and height of this QR Code, measured in modules, between <br />
+    /// 21 and 177 (inclusive). This is equal to version &#xD7; 4 + 17.
+    /// </summary>
+    property Size: Int32 read GetSize;
+
+    /// <summary>
+    /// The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
+    /// Even if a QR Code is created with automatic masking requested (mask =
+    /// &#x2212;1), the resulting object still has a mask value between 0 and 7.
+    /// </summary>
+    property Mask: Int32 read GetMask;
+
+    /// <summary>
+    /// The Modules of this QR Code. Immutable after constructor finishes.
+    /// </summary>
+    property Modules: TQRCodeGenLibInt32Array read GetModules;
+
+    /// <summary>
+    /// property for getting/setting the background color of the QRCode <br />
+    /// Object <br />
+    /// </summary>
+    property BackgroundColor: TColor read GetBackgroundColor
+      write SetBackgroundColor;
+
+    /// <summary>
+    /// property for getting/setting the foreground color of the QRCode
+    /// Object
+    /// </summary>
+    property ForegroundColor: TColor read GetForegroundColor
+      write SetForegroundColor;
+
+{$IFNDEF _FIXINSIGHT_}
+    /// <summary>
+    /// Returns a bitmap image depicting this QR Code, with the specified
+    /// module scale and border modules. For example, ToBmpImage(scale=10,
+    /// border=4) means to pad the QR Code with 4 white border modules on all
+    /// four sides, and use 10×10 pixels to represent each module. The
+    /// resulting image contains the colors specified by the backgroundcolor
+    /// (by default = FFFFFF) and foregroundcolor (by default = 000000)
+    /// properties.
+    /// </summary>
+    /// <param name="AScale">
+    /// the side length (measured in pixels, must be positive) of each module
+    /// </param>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a new bmp image representing this QR Code, with padding and scaling
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the scale or border is out of range, or if <br />{AScale, ABorder,
+    /// FSize} cause the image dimensions to exceed System.High(Int32)
+    /// </exception>
+    /// <remarks>
+    /// <b>The caller is responsible for the lifetime of the returned image
+    /// object.</b>
+    /// </remarks>
+    function ToBmpImage(AScale, ABorder: Int32): TBitmap;
+{$ENDIF}
+    /// <summary>
+    /// Returns a jpeg image depicting this QR Code, with the specified
+    /// module scale and border modules. For example, ToBmpImage(scale=10,
+    /// border=4) means to pad the QR Code with 4 white border modules on all
+    /// four sides, and use 10×10 pixels to represent each module. The
+    /// resulting image contains the colors specified by the backgroundcolor
+    /// (by default = FFFFFF) and foregroundcolor (by default = 000000)
+    /// properties.
+    /// </summary>
+    /// <param name="AScale">
+    /// the side length (measured in pixels, must be positive) of each module
+    /// </param>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a new jpeg image representing this QR Code, with padding and scaling
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the scale or border is out of range, or if <br />{AScale, ABorder,
+    /// FSize} cause the image dimensions to exceed System.High(Int32)
+    /// </exception>
+    /// <remarks>
+    /// <b>The caller is responsible for the lifetime of the returned image
+    /// object.</b>
+    /// </remarks>
+    function ToJpegImage(AScale, ABorder: Int32): TJPEGImage;
+
+{$IFDEF FPC}
+    /// <summary>
+    /// Returns a png image depicting this QR Code, with the specified
+    /// module scale and border modules. For example, ToBmpImage(scale=10,
+    /// border=4) means to pad the QR Code with 4 white border modules on all
+    /// four sides, and use 10×10 pixels to represent each module. The
+    /// resulting image contains the colors specified by the backgroundcolor
+    /// (by default = FFFFFF) and foregroundcolor (by default = 000000)
+    /// properties.
+    /// </summary>
+    /// <param name="AScale">
+    /// the side length (measured in pixels, must be positive) of each module
+    /// </param>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a new png image representing this QR Code, with padding and scaling
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the scale or border is out of range, or if <br />{AScale, ABorder,
+    /// FSize} cause the image dimensions to exceed System.High(Int32)
+    /// </exception>
+    /// <remarks>
+    /// <b>The caller is responsible for the lifetime of the returned image
+    /// object.</b>
+    /// </remarks>
+    function ToPngImage(AScale, ABorder: Int32): TPortableNetworkGraphic;
+{$ENDIF FPC}
+    /// <summary>
+    /// Returns a string of SVG code for an image depicting this QR Code,
+    /// with the specified number of border modules. The string always uses
+    /// Unix newlines AnsiString(#10), regardless of the platform.
+    /// </summary>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <returns>
+    /// a string representing this QR Code as an SVG XML document
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the border is negative
+    /// </exception>
+    function ToSvgString(ABorder: Int32): String;
+
+    /// <summary>
+    /// saves a string of SVG code for an image depicting this QR Code, with
+    /// the specified number of border modules as an svg file. The string
+    /// always uses Unix newlines AnsiString(#10), regardless of the platform.
+    /// </summary>
+    /// <param name="ABorder">
+    /// the number of border modules to add, which must be non-negative
+    /// </param>
+    /// <param name="AFileName">
+    /// the filename to save the output to <b>with file extension</b>
+    /// </param>
+    /// <returns>
+    /// true on success and false on failure.
+    /// </returns>
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the border is negative
+    /// </exception>
+    function ToSvgFile(ABorder: Int32; const AFileName: String): Boolean;
+    /// <summary>
+    /// Returns a QR Code representing the specified Unicode text string at
+    /// the specified error correction level. As a conservative upper bound,
+    /// this function is guaranteed to succeed for strings that have 738 or
+    /// fewer Unicode code points (not UTF-16 code units) if the low error
+    /// correction level is used. The smallest possible QR Code version is
+    /// automatically chosen for the output. The ECC level of the result may
+    /// be higher than the ecl argument if it can be done without increasing
+    /// the version.
+    /// </summary>
+    /// <param name="AText">
+    /// the text to be encoded which can be any Unicode string
+    /// </param>
+    /// <param name="AEcl">
+    /// the error correction level to use (boostable)
+    /// </param>
+    /// <param name="AEncoding">
+    /// the encoding object to be used to convert the text to bytes if the
+    /// text does not fall into numeric or alphanumeric.
+    /// </param>
+    /// <returns>
+    /// a QR Code representing the text
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the text fails to fit in the largest version QR Code at the ECL,
+    /// which means it is too long
+    /// </exception>
+    /// /// <exception cref="QlpQRCodeGenLibTypes|ENullReferenceQRCodeGenLibException">
+    /// if <paramref name="AEncoding" /> is Nil
+    /// </exception>
+    class function EncodeText(const AText: String; AEcl: TEcc;
+      const AEncoding: TEncoding): IQrCode; static;
+
+    /// <summary>
+    /// Returns a QR Code representing the specified binary data at the
+    /// specified error correction level. This function always encodes using
+    /// the binary segment mode, not any text mode. The maximum number of
+    /// bytes allowed is 2953. The smallest possible QR Code version is
+    /// automatically chosen for the output. The ECC level of the result may
+    /// be higher than the ecl argument if it can be done without increasing
+    /// the version.
+    /// </summary>
+    /// <param name="AData">
+    /// the binary data to encode
+    /// </param>
+    /// <param name="AEcl">
+    /// the error correction level to use
+    /// </param>
+    /// <returns>
+    /// a QR Code representing the data
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the data fails to fit in the largest version QR Code at the ECL,
+    /// which means it is too long
+    /// </exception>
+    class function EncodeBinary(const AData: TQRCodeGenLibByteArray; AEcl: TEcc)
+      : IQrCode; static;
+
+    /// <summary>
+    /// Returns a QR Code representing the specified segments at the
+    /// specified error correction level. The smallest possible QR Code
+    /// version is automatically chosen for the output. The ECC level of the
+    /// result may be higher than the ecl argument if it can be done without
+    /// increasing the version. This function allows the user to create a
+    /// custom sequence of segments that switches between modes (such as
+    /// alphanumeric and byte) to encode text in less space.
+    /// </summary>
+    /// <param name="ASegs">
+    /// the segments to encode
+    /// </param>
+    /// <param name="AEcl">
+    /// the error correction level to use (boostable)
+    /// </param>
+    /// <returns>
+    /// a QR Code representing the segments
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the data fails to fit in the largest version QR Code at the ECL,
+    /// which means it is too long
+    /// </exception>
+    class function EncodeSegments(const ASegs
+      : TQRCodeGenLibGenericArray<IQrSegment>; AEcl: TEcc): IQrCode;
+      overload; static;
+
+    /// <summary>
+    /// Returns a QR Code representing the specified segments with the
+    /// specified encoding parameters. The smallest possible QR Code version
+    /// within the specified range is automatically chosen for the output.
+    /// Iff boostEcl is {@code true}, then the ECC level of the result may be
+    /// higher than the ecl argument if it can be done without increasing the
+    /// version. The mask number is either between 0 to 7 (inclusive) to
+    /// force that mask, or −1 to automatically choose an appropriate mask
+    /// (which may be slow). This function allows the user to create a custom
+    /// sequence of segments that switches between modes (such as
+    /// alphanumeric and byte) to encode text in less space.
+    /// </summary>
+    /// <param name="ASegs">
+    /// the segments to encode
+    /// </param>
+    /// <param name="AEcl">
+    /// the error correction level to use (boostable)
+    /// </param>
+    /// <param name="AMinVersion">
+    /// the minimum allowed version of the QR Code (at least 1)
+    /// </param>
+    /// <param name="AMaxVersion">
+    /// the maximum allowed version of the QR Code (at most 40)
+    /// </param>
+    /// <param name="AMask">
+    /// the mask number to use (between 0 and 7 (inclusive)), or −1 for
+    /// automatic mask
+    /// </param>
+    /// <param name="ABoostEcl">
+    /// increases the ECC level as long as it doesn't increase the version
+    /// number
+    /// </param>
+    /// <returns>
+    /// a QR Code representing the segments
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if 1 &#x2264; minVersion &#x2264; maxVersion &#x2264; 40
+    /// or &#x2212;1 &#x2264; mask &#x2264; 7 is violated; or if the segments fail to
+    /// fit in the maxVersion QR Code at the ECL, which means they are too long
+    /// </exception>
+    class function EncodeSegments(const ASegs
+      : TQRCodeGenLibGenericArray<IQrSegment>; AEcl: TEcc;
+      AMinVersion, AMaxVersion, AMask: Int32; ABoostEcl: Boolean): IQrCode;
+      overload; static;
+
+  end;
+
+implementation
+
+{ TQrCode }
+
+class function TQrCode.GetRValue(Argb: UInt32): Byte;
+begin
+  Result := Byte(Argb);
+end;
+
+class function TQrCode.GetGValue(Argb: UInt32): Byte;
+begin
+  Result := Byte(Argb shr 8);
+end;
+
+class function TQrCode.GetBValue(Argb: UInt32): Byte;
+begin
+  Result := Byte(Argb shr 16);
+end;
+
+class function TQrCode.TColorToHTMLColorHex(const AColor: TColor): String;
+begin
+  // Result := IntToHex(GetRValue(ColorToRGB(AColor)), 2) +
+  // IntToHex(GetGValue(ColorToRGB(AColor)), 2) +
+  // IntToHex(GetBValue(ColorToRGB(AColor)), 2);
+  Result := Format('%.2x%.2x%.2x', [GetRValue(ColorToRGB(AColor)),
+    GetGValue(ColorToRGB(AColor)), GetBValue(ColorToRGB(AColor))]);
+end;
+
+function TQrCode.GetVersion: Int32;
+begin
+  Result := FVersion;
+end;
+
+function TQrCode.HandleConstructorMasking(const AMasks
+  : TQRCodeGenLibMatrixInt32Array; AMask: Int32): Int32;
+var
+  LMinPenalty, LIdx, LPenalty: Int32;
+begin
+  if (AMask = -1) then
+  begin
+    // Automatically choose best mask
+    LMinPenalty := System.High(Int32);
+    for LIdx := 0 to System.Pred(8) do
+    begin
+      DrawFormatBits(LIdx);
+      ApplyMask(AMasks[LIdx]);
+      LPenalty := GetPenaltyScore();
+      if (LPenalty < LMinPenalty) then
+      begin
+        AMask := LIdx;
+        LMinPenalty := LPenalty;
+      end;
+      ApplyMask(AMasks[LIdx]); // Undoes the mask due to XOR
+    end;
+  end;
+{$IFDEF DEBUG}
+  System.Assert((0 <= AMask) and (AMask <= 7));
+{$ENDIF DEBUG}
+  DrawFormatBits(AMask); // Overwrite old format bits
+  ApplyMask(AMasks[AMask]); // Apply the final choice of mask
+  // The caller shall assign this value to the final-declared field
+  Result := AMask;
+end;
+
+function TQrCode.GetSize: Int32;
+begin
+  Result := FSize;
+end;
+
+function TQrCode.GetMask: Int32;
+begin
+  Result := FMask;
+end;
+
+function TQrCode.GetModules: TQRCodeGenLibInt32Array;
+begin
+  Result := System.Copy(FModules);
+end;
+
+class function TQrCode.GetNumDataCodeWords(AVersion: Int32; AEcl: TEcc): Int32;
+begin
+  Result := (TBits.Asr32(TQrTemplate.GetNumRawDataModules(AVersion), 3)) -
+    (ECC_CODEWORDS_PER_BLOCK[AEcl.ToInt32][AVersion] *
+    NUM_ERROR_CORRECTION_BLOCKS[AEcl.ToInt32][AVersion]);
+end;
+
+function TQrCode.GetPenaltyScore: Int32;
+var
+  LEnd, LBlack, LIndex, LDownIndex, LBits, LDownBits, LRunColor, LRunLen, Lx,
+    LBit, LTotal, Lk, Ly: Int32;
+begin
+  Result := 0;
+  LBlack := 0;
+  LIndex := 0;
+  LDownIndex := FSize;
+  LEnd := FSize * FSize;
+
+  // Iterate over adjacent pairs of rows
+  while LIndex < LEnd do
+  begin
+
+    LBits := 0;
+    LDownBits := 0;
+    LRunColor := 0;
+    LRunLen := 0;
+
+    Lx := 0;
+    while Lx < FSize do
+    begin
+
+      // Adjacent modules having same color
+      LBit := TQrCodeCommons.GetBit(FModules[TBits.Asr32(LIndex, 5)], LIndex);
+      if (LBit <> LRunColor) then
+      begin
+        LRunColor := LBit;
+        LRunLen := 1;
+      end
+      else
+      begin
+        System.Inc(LRunLen);
+        if (LRunLen = 5) then
+        begin
+          Result := Result + PENALTY_N1;
+        end
+        else if (LRunLen > 5) then
+        begin
+          System.Inc(Result);
+        end;
+      end;
+
+      LBlack := LBlack + LBit;
+      LBits := ((LBits and 1023) shl 1) or LBit;
+      if (LDownIndex < LEnd) then
+      begin
+        LDownBits := ((LDownBits and 1) shl 1) or
+          TQrCodeCommons.GetBit(FModules[TBits.Asr32(LDownIndex, 5)],
+          LDownIndex);
+        // 2*2 blocks of modules having same color
+        if ((Lx >= 1) and ((LDownBits = 0) or (LDownBits = 3)) and
+          (LDownBits = (LBits and 3))) then
+        begin
+          Result := Result + PENALTY_N2;
+        end;
+      end;
+
+      // Finder-like pattern
+      if ((Lx >= 10) and ((LBits = 93) or (LBits = 1488))) then
+      begin
+        Result := Result + PENALTY_N3;
+      end;
+
+      System.Inc(Lx);
+      System.Inc(LIndex);
+      System.Inc(LDownIndex);
+    end;
+
+  end;
+
+  // Iterate over single columns
+  Lx := 0;
+  while Lx < FSize do
+  begin
+    LBits := 0;
+    LRunColor := 0;
+    LRunLen := 0;
+    Ly := 0;
+    LIndex := Lx;
+    while Ly < FSize do
+    begin
+      // Adjacent modules having same color
+      LBit := TQrCodeCommons.GetBit(FModules[TBits.Asr32(LIndex, 5)], LIndex);
+      if (LBit <> LRunColor) then
+      begin
+        LRunColor := LBit;
+        LRunLen := 1;
+      end
+      else
+      begin
+        System.Inc(LRunLen);
+        if (LRunLen = 5) then
+        begin
+          Result := Result + PENALTY_N1;
+        end
+        else if (LRunLen > 5) then
+        begin
+          System.Inc(Result);
+        end;
+      end;
+
+      // Finder-like pattern
+      LBits := ((LBits and 1023) shl 1) or LBit;
+      if ((Ly >= 10) and ((LBits = 93) or (LBits = 1488))) then
+      begin
+        Result := Result + PENALTY_N3;
+      end;
+      System.Inc(Ly);
+      System.Inc(LIndex, FSize);
+    end;
+    System.Inc(Lx);
+  end;
+
+  // Balance of black and white modules
+  LTotal := FSize * FSize; // Note that size is odd, so black/total != 1/2
+  // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
+  Lk := ((Abs((LBlack * 20) - (LTotal * 10)) + LTotal - 1) div LTotal) - 1;
+  Result := Result + (Lk * PENALTY_N4);
+
+end;
+
+function TQrCode.GetModule(Ax, Ay: Int32): Boolean;
+var
+  LIdx: Int32;
+begin
+  if ((0 <= Ax) and (Ax < FSize) and (0 <= Ay) and (Ay < FSize)) then
+  begin
+    LIdx := (Ay * FSize) + Ax;
+    Result := TQrCodeCommons.GetBit(FModules[TBits.Asr32(LIdx, 5)], LIdx) <> 0;
+  end
+  else
+  begin
+    Result := false;
+  end;
+end;
+
+function TQrCode.AddEccAndInterleave(const AData: TQRCodeGenLibByteArray)
+  : TQRCodeGenLibByteArray;
+var
+  LNumBlocks, LBlockEccLen, LRawCodewords, LNumShortBlocks, LShortBlockDataLen,
+    LIIdx, LKIdx, LJIdx, LLIdx, LDatLen, LCompareResult: Int32;
+  LRs: IReedSolomonGenerator;
+  LEcc: TQRCodeGenLibByteArray;
+begin
+  if (System.Length(AData) <> GetNumDataCodeWords(FVersion,
+    FErrorCorrectionLevel)) then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.Create('');
+  end;
+
+  // Calculate parameter numbers
+  LNumBlocks := NUM_ERROR_CORRECTION_BLOCKS[FErrorCorrectionLevel.ToInt32]
+    [FVersion];
+  LBlockEccLen := ECC_CODEWORDS_PER_BLOCK[FErrorCorrectionLevel.ToInt32]
+    [FVersion];
+  LRawCodewords := TBits.Asr32(TQrTemplate.GetNumRawDataModules(FVersion), 3);
+  LNumShortBlocks := LNumBlocks - (LRawCodewords mod LNumBlocks);
+  LShortBlockDataLen := (LRawCodewords div LNumBlocks) - LBlockEccLen;
+
+  // Split data into blocks, calculate ECC, and interleave
+  // (not concatenate) the bytes into a single sequence
+  System.SetLength(Result, LRawCodewords);
+  LRs := TReedSolomonGenerator.GetInstance(LBlockEccLen);
+  System.SetLength(LEcc, LBlockEccLen); // Temporary storage per iteration
+
+  LIIdx := 0;
+  LKIdx := 0;
+  while LIIdx < LNumBlocks do
+  begin
+    if LIIdx < LNumShortBlocks then
+    begin
+      LCompareResult := 0;
+    end
+    else
+    begin
+      LCompareResult := 1;
+    end;
+    LDatLen := LShortBlockDataLen + LCompareResult;
+    LRs.GetRemainder(AData, LKIdx, LDatLen, LEcc);
+    LJIdx := 0;
+    LLIdx := LIIdx;
+    while LJIdx < LDatLen do
+    begin
+      // Copy data
+      if (LJIdx = LShortBlockDataLen) then
+      begin
+        LLIdx := LLIdx - LNumShortBlocks;
+      end;
+      Result[LLIdx] := AData[LKIdx];
+      System.Inc(LJIdx);
+      System.Inc(LKIdx);
+      System.Inc(LLIdx, LNumBlocks);
+    end;
+
+    LJIdx := 0;
+    LLIdx := System.Length(AData) + LIIdx;
+    while LJIdx < LBlockEccLen do
+    begin
+      // Copy ECC
+      Result[LLIdx] := LEcc[LJIdx];
+      System.Inc(LJIdx);
+      System.Inc(LLIdx, LNumBlocks);
+    end;
+    System.Inc(LIIdx);
+  end;
+end;
+
+procedure TQrCode.ApplyMask(const AMask: TQRCodeGenLibInt32Array);
+var
+  LIdx: Int32;
+begin
+  if (System.Length(AMask) <> System.Length(FModules)) then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.Create('');
+  end;
+  for LIdx := System.Low(AMask) to System.High(AMask) do
+  begin
+    FModules[LIdx] := FModules[LIdx] xor AMask[LIdx];
+  end;
+end;
+
+constructor TQrCode.Create(AVersion: Int32; AEcl: TEcc;
+  const ADataCodewords: TQRCodeGenLibByteArray; AMask: Int32);
+var
+  LTpl: IQrTemplate;
+  LAllCodewords: TQRCodeGenLibByteArray;
+begin
+  Inherited Create();
+  // Check arguments and initialize fields
+  FErrorCorrectionLevel := AEcl;
+
+  if ((AVersion < MIN_VERSION) or (AVersion > MAX_VERSION)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes(@SValueOutOfRange);
+  end;
+
+  if ((FMask < -1) or (FMask > 7)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes(@SMaskOutOfRange);
+  end;
+
+  // Initialize fields
+  FVersion := AVersion;
+  FSize := (AVersion * 4) + 17;
+
+  LTpl := TQrTemplate.GetInstance(AVersion);
+  // no need to make a copy here because the property already does that for us.
+  FModules := LTpl.Template;
+
+  // Compute ECC, draw modules, do masking
+  LAllCodewords := AddEccAndInterleave(ADataCodewords);
+  DrawCodeWords(LTpl.DataOutputBitIndexes, LAllCodewords);
+  FMask := HandleConstructorMasking(LTpl.Masks, AMask);
+  FBackgroundColor := clWhite;
+  FForegroundColor := clBlack;
+end;
+
+procedure TQrCode.SetModule(Ax, Ay, ABlack: Int32);
+var
+  LIdx: Int32;
+begin
+{$IFDEF DEBUG}
+  System.Assert((0 <= Ax) and (Ax < FSize));
+  System.Assert((0 <= Ay) and (Ay < FSize));
+{$ENDIF DEBUG}
+  LIdx := (Ay * FSize) + Ax;
+  if (ABlack = 0) then
+  begin
+    FModules[TBits.Asr32(LIdx, 5)] := FModules[TBits.Asr32(LIdx, 5)] and
+      (not(1 shl LIdx));
+  end
+  else if (ABlack = 1) then
+  begin
+    FModules[TBits.Asr32(LIdx, 5)] := FModules[TBits.Asr32(LIdx, 5)] or
+      (1 shl LIdx);
+  end
+  else
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.Create('');
+  end;
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+function TQrCode.ToBmpImage(AScale, ABorder: Int32): TBitmap;
+var
+  LColumn, LRow: Int32;
+  LDoColor: Boolean;
+  LBrushColor: TColor;
+begin
+  ValidateImageDimensions(AScale, ABorder);
+
+  Result := TBitmap.Create;
+
+  Result.SetSize((FSize + (ABorder * 2)) * AScale, (FSize + (ABorder * 2))
+    * AScale);
+  try
+{$IFDEF FPC}
+    // update locking for speedup. only available in FPC
+    Result.BeginUpdate(True);
+{$ENDIF FPC}
+    for LColumn := 0 to System.Pred(Result.Height) do
+    begin
+      for LRow := 0 to System.Pred(Result.Width) do
+      begin
+        LDoColor := GetModule((LRow div AScale) - ABorder,
+          (LColumn div AScale) - ABorder);
+        if LDoColor then
+        begin
+          LBrushColor := FForegroundColor;
+        end
+        else
+        begin
+          LBrushColor := FBackgroundColor;
+        end;
+        Result.Canvas.Pixels[LRow, LColumn] := LBrushColor;
+      end;
+    end;
+  finally
+{$IFDEF FPC}
+    // update locking for speedup. only available in FPC
+    Result.EndUpdate(false);
+{$ENDIF FPC}
+  end;
+end;
+{$ENDIF}
+
+function TQrCode.ToJpegImage(AScale, ABorder: Int32): TJPEGImage;
+var
+  LBitmap: TBitmap;
+begin
+  LBitmap := ToBmpImage(AScale, ABorder);
+  Result := TJPEGImage.Create;
+  Result.CompressionQuality := 99;
+  try
+    Result.Assign(LBitmap);
+  finally
+    LBitmap.Free;
+  end;
+end;
+
+{$IFDEF FPC}
+
+function TQrCode.ToPngImage(AScale, ABorder: Int32): TPortableNetworkGraphic;
+var
+  LBitmap: TBitmap;
+begin
+  LBitmap := ToBmpImage(AScale, ABorder);
+  Result := TPortableNetworkGraphic.Create;
+  try
+    Result.Assign(LBitmap);
+  finally
+    LBitmap.Free;
+  end;
+end;
+
+{$ENDIF FPC}
+
+function TQrCode.ToSvgString(ABorder: Int32): String;
+var
+  LColumn, LRow: Int32;
+  LBorder: Int64;
+  LStringList: TStringList;
+begin
+  if (ABorder < 0) then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.CreateRes(@SBorderNegative);
+  end;
+  LBorder := ABorder;
+  LStringList := TStringList.Create;
+  LStringList.LineBreak := '';
+  try
+    LStringList.Add(Format('<?xml version="1.0" encoding="UTF-8"?>%s',
+      [UNIX_NEW_LINE]));
+    LStringList.Add
+      (Format('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">%s',
+      [UNIX_NEW_LINE]));
+    LStringList.Add
+      (Format('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 %d %d" stroke="none">%s',
+      [FSize + (LBorder * 2), (FSize + LBorder * 2), UNIX_NEW_LINE]));
+    LStringList.Add
+      (Format('%s<rect width="100%%" height="100%%" fill="#%s"/>%s',
+      [TAB, TColorToHTMLColorHex(FBackgroundColor), UNIX_NEW_LINE]));
+    LStringList.Add(Format('%s<path d="', [TAB]));
+
+    for LColumn := 0 to System.Pred(FSize) do
+    begin
+      for LRow := 0 to System.Pred(FSize) do
+      begin
+        if (GetModule(LRow, LColumn)) then
+        begin
+          if ((LRow <> 0) or (LColumn <> 0)) then
+          begin
+            LStringList.Add(' ');
+          end;
+          LStringList.Add(Format('M%d,%dh1v1h-1z', [LRow + LBorder,
+            LColumn + LBorder]));
+        end;
+      end;
+
+    end;
+
+    LStringList.Add(Format('" fill="#%s"/>%s',
+      [TColorToHTMLColorHex(FForegroundColor), UNIX_NEW_LINE]));
+    LStringList.Add(Format('</svg>%s', [UNIX_NEW_LINE]));
+
+    Result := LStringList.Text;
+  finally
+    LStringList.Free;
+  end;
+end;
+
+function TQrCode.ToSvgFile(ABorder: Int32; const AFileName: String): Boolean;
+var
+  LFileStream: TFileStream;
+  LBytes: TQRCodeGenLibByteArray;
+begin
+  if Trim(AFileName) = '' then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.CreateRes(@SFileNameEmpty);
+  end;
+  LFileStream := TFileStream.Create(AFileName, fmCreate);
+  try
+    try
+      LBytes := TConverters.ConvertStringToBytes(ToSvgString(ABorder),
+        TEncoding.UTF8);
+      LFileStream.WriteBuffer(LBytes[0], System.Length(LBytes) *
+        System.SizeOf(Byte));
+      Result := True;
+    except
+      Result := false;
+    end;
+  finally
+    LFileStream.Free;
+  end;
+end;
+
+procedure TQrCode.ValidateImageDimensions(AScale, ABorder: Int32);
+begin
+  if ((AScale <= 0) or (ABorder < 0)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes(@SValueOutOfRange);
+  end;
+
+  if ((ABorder > (System.High(Int32) shr 1)) or
+    ((FSize + (ABorder * Int64(2))) > (System.High(Int32) div AScale))) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SScaleOrBorderTooLarge);
+  end;
+end;
+
+procedure TQrCode.DrawCodeWords(const ADataOutputBitIndexes
+  : TQRCodeGenLibInt32Array; const AAllCodewords: TQRCodeGenLibByteArray);
+var
+  LIIdx, LJIdx, LBit: Int32;
+begin
+  if ((System.Length(AAllCodewords) * 8) <>
+    (System.Length(ADataOutputBitIndexes))) then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.Create('');
+  end;
+  for LIIdx := System.Low(ADataOutputBitIndexes)
+    to System.High(ADataOutputBitIndexes) do
+
+  begin
+    LJIdx := ADataOutputBitIndexes[LIIdx];
+    LBit := TQrCodeCommons.GetBit(AAllCodewords[TBits.Asr32(LIIdx, 3)],
+      (not LIIdx) and 7);
+    FModules[TBits.Asr32(LJIdx, 5)] := FModules[TBits.Asr32(LJIdx, 5)] or
+      (LBit shl LJIdx);
+  end;
+end;
+
+procedure TQrCode.DrawFormatBits(AMask: Int32);
+var
+  LData, LRem, LIdx, LBits: Int32;
+begin
+  // Calculate error correction code and pack bits
+  LData := (FErrorCorrectionLevel.FormatBits shl 3) or AMask;
+  // errCorrLvl is uint2, mask is uint3
+  LRem := LData;
+  LIdx := 0;
+  while LIdx < 10 do
+  begin
+    LRem := (LRem shl 1) xor ((TBits.Asr32(LRem, 9)) * $537);
+    System.Inc(LIdx);
+  end;
+  LBits := ((LData shl 10) or LRem) xor $5412; // uint15
+{$IFDEF DEBUG}
+  System.Assert(TBits.Asr32(LBits, 15) = 0);
+{$ENDIF DEBUG}
+  // Draw first copy
+  for LIdx := 0 to 5 do
+  begin
+    SetModule(8, LIdx, TQrCodeCommons.GetBit(LBits, LIdx));
+  end;
+
+  SetModule(8, 7, TQrCodeCommons.GetBit(LBits, 6));
+  SetModule(8, 8, TQrCodeCommons.GetBit(LBits, 7));
+  SetModule(7, 8, TQrCodeCommons.GetBit(LBits, 8));
+
+  for LIdx := 9 to System.Pred(15) do
+  begin
+    SetModule(14 - LIdx, 8, TQrCodeCommons.GetBit(LBits, LIdx));
+  end;
+
+  // Draw second copy
+  for LIdx := 0 to 7 do
+  begin
+    SetModule(FSize - 1 - LIdx, 8, TQrCodeCommons.GetBit(LBits, LIdx));
+  end;
+  for LIdx := 8 to System.Pred(15) do
+  begin
+    SetModule(8, FSize - 15 + LIdx, TQrCodeCommons.GetBit(LBits, LIdx));
+  end;
+  SetModule(8, FSize - 8, 1); // Always black
+end;
+
+class function TQrCode.EncodeBinary(const AData: TQRCodeGenLibByteArray;
+  AEcl: TEcc): IQrCode;
+begin
+  Result := EncodeSegments(TQRCodeGenLibGenericArray<IQrSegment>.Create
+    (TQrSegment.MakeBytes(AData)), AEcl);
+end;
+
+class function TQrCode.EncodeSegments(const ASegs
+  : TQRCodeGenLibGenericArray<IQrSegment>; AEcl: TEcc): IQrCode;
+begin
+  Result := EncodeSegments(ASegs, AEcl, MIN_VERSION, MAX_VERSION, -1, True);
+end;
+
+class function TQrCode.EncodeSegments(const ASegs
+  : TQRCodeGenLibGenericArray<IQrSegment>; AEcl: TEcc;
+  AMinVersion, AMaxVersion, AMask: Int32; ABoostEcl: Boolean): IQrCode;
+var
+  LVersion, LDataUsedBits, LDataCapacityBits, LPadByte: Int32;
+  LNewEcl: TEcc;
+  LBitBuffer: TBitBuffer;
+  LSeg: IQrSegment;
+begin
+  if (not((MIN_VERSION <= AMinVersion) and (AMinVersion <= AMaxVersion) and
+    (AMaxVersion <= MAX_VERSION)) or (AMask < -1) or (AMask > 7)) then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.CreateRes(@SInvalidValue);
+  end;
+  LVersion := AMinVersion;
+  // Find the minimal version number to use
+  while True do
+  begin
+    LDataCapacityBits := GetNumDataCodeWords(LVersion, AEcl) * 8;
+    // Number of data bits available
+    LDataUsedBits := TQrSegment.GetTotalBits(ASegs, LVersion);
+    if ((LDataUsedBits <> -1) and (LDataUsedBits <= LDataCapacityBits)) then
+    begin
+      Break; // This version number is found to be suitable
+    end;
+    if (LVersion >= AMaxVersion) then
+    // All versions in the range could not fit the given data
+    begin
+      raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes(@SDataTooLong);
+    end;
+    System.Inc(LVersion);
+  end;
+
+  // Increase the error correction level while the data still fits in the current version number
+
+  for LNewEcl in EccOrder do
+  begin
+    // From low to high
+    if ((ABoostEcl) and (LDataUsedBits <= (GetNumDataCodeWords(LVersion,
+      LNewEcl) * 8))) then
+    begin
+      AEcl := LNewEcl;
+    end;
+  end;
+
+  // Concatenate all segments to create the data bit string
+  LBitBuffer := TBitBuffer.Create();
+  for LSeg in ASegs do
+  begin
+    LBitBuffer.AppendBits(LSeg.Mode.ModeBits, 4);
+    LBitBuffer.AppendBits(LSeg.NumChars, LSeg.Mode.NumCharCountBits(LVersion));
+    LBitBuffer.AppendBits(LSeg.Data, LSeg.BitLength);
+  end;
+
+{$IFDEF DEBUG}
+  System.Assert(LBitBuffer.BitLength = LDataUsedBits);
+{$ENDIF DEBUG}
+  // Add terminator and pad up to a byte if applicable
+  LDataCapacityBits := GetNumDataCodeWords(LVersion, AEcl) * 8;
+{$IFDEF DEBUG}
+  System.Assert(LBitBuffer.BitLength <= LDataCapacityBits);
+{$ENDIF DEBUG}
+  LBitBuffer.AppendBits(0, Min(4, LDataCapacityBits - LBitBuffer.BitLength));
+  LBitBuffer.AppendBits(0, (8 - (LBitBuffer.BitLength and 7)) and 7);
+{$IFDEF DEBUG}
+  System.Assert((LBitBuffer.BitLength and 7) = 0);
+{$ENDIF DEBUG}
+  // Pad with alternating bytes until data capacity is reached
+  LPadByte := $EC;
+  while LBitBuffer.BitLength < LDataCapacityBits do
+  begin
+    LBitBuffer.AppendBits(LPadByte, 8);
+    LPadByte := LPadByte xor ($EC xor $11);
+  end;
+
+  // Create the QR Code symbol
+  Result := TQrCode.Create(LVersion, AEcl, LBitBuffer.GetBytes(), AMask);
+
+end;
+
+class function TQrCode.EncodeText(const AText: String; AEcl: TEcc;
+  const AEncoding: TEncoding): IQrCode;
+begin
+  TGuard.RequireNotNull(AEncoding, SEncodingInstanceNil);
+  Result := EncodeSegments(TQrSegment.MakeSegments(AText, AEncoding), AEcl);
+end;
+
+function TQrCode.GetBackgroundColor: TColor;
+begin
+  Result := FBackgroundColor;
+end;
+
+function TQrCode.GetForegroundColor: TColor;
+begin
+  Result := FForegroundColor;
+end;
+
+procedure TQrCode.SetBackgroundColor(const AColor: TColor);
+begin
+  FBackgroundColor := AColor;
+end;
+
+procedure TQrCode.SetForegroundColor(const AColor: TColor);
+begin
+  FForegroundColor := AColor;
+end;
+
+{ TQrCode.TEccHelper }
+
+function TQrCode.TEccHelper.GetFormatBits: Int32;
+begin
+  Result := Ord(Self);
+end;
+
+function TQrCode.TEccHelper.GetToInt32: Int32;
+begin
+  case Self of
+    TQrCode.TEcc.eccLow:
+      begin
+        Result := 0;
+        Exit;
+      end;
+    TQrCode.TEcc.eccMedium:
+      begin
+        Result := 1;
+        Exit;
+      end;
+    TQrCode.TEcc.eccQuartile:
+      begin
+        Result := 2;
+        Exit;
+      end;
+    TQrCode.TEcc.eccHigh:
+      begin
+        Result := 3;
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationQRCodeGenLibException.CreateRes(@SInvalidState);
+    end;
+  end;
+end;
+
+end.

+ 48 - 0
QRCodeGenLib/src/QRCodeGen/QlpQrCodeCommons.pas

@@ -0,0 +1,48 @@
+unit QlpQrCodeCommons;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  QlpBits;
+
+type
+
+  /// <summary>
+  /// Class to hold common constants and methods used between QrCode class
+  /// and other classes to prevent the <b>dreaded circular reference.</b>
+  /// </summary>
+  TQrCodeCommons = class sealed(TObject)
+
+  public
+
+    const
+
+    /// <summary>
+    /// The minimum version number (1) supported in the QR Code Model 2
+    /// standard.
+    /// </summary>
+    MIN_VERSION = Int32(1);
+
+    /// <summary>
+    /// The maximum version number (40) supported in the QR Code Model 2
+    /// standard.
+    /// </summary>
+    MAX_VERSION = Int32(40);
+
+    // Returns 0 or 1 based on the Ai'th bit of Ax.
+    class function GetBit(Ax, Ai: Int32): Int32; inline;
+
+  end;
+
+implementation
+
+{ TQrCodeCommons }
+
+class function TQrCodeCommons.GetBit(Ax, Ai: Int32): Int32;
+begin
+  result := (TBits.Asr32(Ax, Ai)) and 1;
+end;
+
+end.

+ 561 - 0
QRCodeGenLib/src/QRCodeGen/QlpQrSegment.pas

@@ -0,0 +1,561 @@
+unit QlpQrSegment;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  Math,
+  SysUtils,
+  QlpIQrSegment,
+  QlpQrSegmentMode,
+  QlpBitBuffer,
+  QlpGuard,
+  QlpBits,
+  QlpArrayUtils,
+  QlpConverters,
+  QlpQRCodeGenLibTypes;
+
+resourcestring
+  SInvalidValue = 'Invalid value';
+  SECIAssignmentOutOfRange = 'ECI assignment value out of range';
+  SUnEncodableCharacters =
+    'String contains unencodable characters in alphanumeric mode';
+  SNonNumericCharacters = 'String contains non-numeric characters';
+  SDataTooLong = 'Data too long';
+  SEncodingInstanceNil = 'Encoding instance cannot be nil';
+  SSegmentInstanceNil = 'Segment instance cannot be nil';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// A segment of character/binary/control data in a QR Code symbol.
+  /// Instances of this class are immutable.
+  /// </para>
+  /// <para>
+  /// The mid-level way to create a segment is to take the payload data
+  /// and call a static factory function such as <see cref="QlpQrSegment|TQrSegment.MakeNumeric(string)">
+  /// TQrSegment.MakeNumeric(String)</see>.
+  /// </para>
+  /// <para>
+  /// The low-level way to create a segment is to custom-make the bit
+  /// buffer and call the <see cref="QlpQrSegment|TQrSegment.Create(TQrSegmentMode,Int32,TQRCodeGenLibInt32Array,Int32)">
+  /// TQrSegment Constructor</see> with appropriate values.
+  /// </para>
+  /// <para>
+  /// <br />This segment class imposes no length restrictions, but QR
+  /// Codes have restrictions. <br />Even in the most favorable
+  /// conditions, a QR Code can only hold 7089 characters of data. <br />
+  /// Any segment longer than this is meaningless for the purpose of
+  /// generating QR Codes. <br />This class can represent kanji mode
+  /// segments, but provides no help in encoding them.
+  /// </para>
+  /// </summary>
+  TQrSegment = class sealed(TInterfacedObject, IQrSegment)
+
+  strict private
+
+  const
+    ALPHANUMERIC_CHARSET
+      : String = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
+
+    class var
+
+      FALPHANUMERIC_MAP: TQRCodeGenLibInt32Array;
+
+    class constructor QRSegment();
+
+    function GetMode: TQrSegmentMode; inline;
+    function GetNumChars: Int32; inline;
+    function GetBitLength: Int32; inline;
+    function GetData: TQRCodeGenLibInt32Array; inline;
+
+  public
+
+  var
+    FMode: TQrSegmentMode;
+    // FBitLength Requires 0 <= FBitLength <= System.Length(FData) * 32.
+    FNumChars, FBitLength: Int32;
+    FData: TQRCodeGenLibInt32Array;
+
+    /// <summary>
+    /// Constructs a QR Code segment with the specified attributes and data. <br />
+    /// The character count (ANumChars) must agree with the mode and the bit
+    /// buffer length, <br />but the constraint isn't checked. The specified
+    /// bit buffer is cloned and stored.
+    /// </summary>
+    /// <param name="AMode">
+    /// the mode
+    /// </param>
+    /// <param name="ANumChars">
+    /// the data length in characters or bytes, which is non-negative
+    /// </param>
+    /// <param name="AData">
+    /// the data bits
+    /// </param>
+    /// <param name="ABitLength">
+    /// the number of valid prefix bits in the data array
+    /// </param>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentInvalidQRCodeGenLibException">
+    /// if the character count is negative
+    /// </exception>
+    constructor Create(AMode: TQrSegmentMode; ANumChars: Int32;
+      const AData: TQRCodeGenLibInt32Array; ABitLength: Int32);
+
+    /// <summary>
+    /// Mode used by this Segment.
+    /// </summary>
+    property Mode: TQrSegmentMode read GetMode;
+
+    /// <summary>
+    /// The length of this segment's unencoded data. Measured in characters
+    /// for <br />numeric/alphanumeric/kanji mode, bytes for byte mode, and 0
+    /// for ECI mode. <br />Always zero or positive. Not the same as the
+    /// data's bit length.
+    /// </summary>
+    property NumChars: Int32 read GetNumChars;
+
+    /// <summary>
+    /// Requires 0 &lt;= bitLength &lt;= data.length * 32.
+    /// </summary>
+    property BitLength: Int32 read GetBitLength;
+
+    /// <summary>
+    /// The data bits of this segment.
+    /// </summary>
+    property Data: TQRCodeGenLibInt32Array read GetData;
+
+    /// <summary>
+    /// Tests whether the specified string can be encoded as a segment in
+    /// numeric mode. <br />A string is encodable if each character is in the
+    /// range 0 to 9.
+    /// </summary>
+    /// <param name="AText">
+    /// the string to test for encodability
+    /// </param>
+    /// <returns>
+    /// <c>true</c> if each character is in the range 0 to 9.
+    /// </returns>
+    class function IsNumeric(const AText: String): Boolean; static;
+
+    /// <summary>
+    /// Tests whether the specified string can be encoded as a segment in
+    /// alphanumeric mode. <br />A string is encodable iff each character is
+    /// in the following set: 0 to 9, A to Z <br />(uppercase only), space,
+    /// dollar, percent, asterisk, plus, hyphen, period, slash, colon.
+    /// </summary>
+    /// <param name="AText">
+    /// the string to test for encodability
+    /// </param>
+    /// <returns>
+    /// <c>true</c> if each character is in the alphanumeric mode character
+    /// set
+    /// </returns>
+    class function IsAlphaNumeric(const AText: String): Boolean; static;
+
+    // Calculates the number of bits needed to encode the given segments at the given version.
+    // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too
+    // many characters to fit its length field, or the total bits exceeds System.High(Int32).
+    class function GetTotalBits(const ASegments
+      : TQRCodeGenLibGenericArray<IQrSegment>; AVersion: Int32): Int32; static;
+
+    /// <summary>
+    /// <para>
+    /// Returns a segment representing the specified binary data <br />
+    /// encoded in byte mode. All input byte arrays are acceptable.
+    /// </para>
+    /// <para>
+    /// Any text string can be converted to bytes via <c>TEncoding</c>
+    /// and encoded as a byte mode segment.
+    /// </para>
+    /// </summary>
+    /// <param name="AData">
+    /// the binary data
+    /// </param>
+    /// <returns>
+    /// a segment containing the data
+    /// </returns>
+    class function MakeBytes(const AData: TQRCodeGenLibByteArray)
+      : IQrSegment; static;
+
+    /// <summary>
+    /// Returns a segment representing the specified string of decimal digits
+    /// encoded in numeric mode.
+    /// </summary>
+    /// <param name="ADigits">
+    /// the text with only digits from 0 to 9 allowed
+    /// </param>
+    /// <returns>
+    /// a segment containing the text
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentInvalidQRCodeGenLibException">
+    /// if the string contains non-digit characters
+    /// </exception>
+    class function MakeNumeric(const ADigits: String): IQrSegment; static;
+
+    /// <summary>
+    /// Returns a segment representing the specified text string encoded in
+    /// alphanumeric mode. <br />The characters allowed are: 0 to 9, A to Z
+    /// (uppercase only), space, <br />dollar, percent, asterisk, plus,
+    /// hyphen, period, slash, colon.
+    /// </summary>
+    /// <param name="AText">
+    /// the text with only certain characters allowed
+    /// </param>
+    /// <returns>
+    /// a segment containing the text
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentInvalidQRCodeGenLibException">
+    /// if the string contains non-encodable characters
+    /// </exception>
+    class function MakeAlphaNumeric(const AText: String): IQrSegment; static;
+
+    /// <summary>
+    /// Returns a segment representing an Extended Channel Interpretation <br />
+    /// (ECI) designator with the specified assignment value.
+    /// </summary>
+    /// <param name="AAssignValue">
+    /// the ECI assignment number (see the AIM ECI specification)
+    /// </param>
+    /// <returns>
+    /// a segment containing the data
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|EArgumentOutOfRangeQRCodeGenLibException">
+    /// if the value is outside the range [0, 10<sup>6</sup>)
+    /// </exception>
+    class function MakeEci(const AAssignValue: Int32): IQrSegment; static;
+
+    /// <summary>
+    /// * Returns an array of zero or more segments to represent the
+    /// specified Unicode text string. <br />The result may use various
+    /// segment modes and switch modes to optimize the length of the bit
+    /// stream.
+    /// </summary>
+    /// <param name="AText">
+    /// the text to be encoded, which can be any Unicode string
+    /// </param>
+    /// <param name="AEncoding">
+    /// the encoding object to be used to convert the text to bytes if the
+    /// text does not fall into numeric or alphanumeric.
+    /// </param>
+    /// <returns>
+    /// an array of segments containing the text
+    /// </returns>
+    /// <exception cref="QlpQRCodeGenLibTypes|ENullReferenceQRCodeGenLibException">
+    /// if <paramref name="AEncoding" /> is Nil
+    /// </exception>
+    class function MakeSegments(const AText: String; const AEncoding: TEncoding)
+      : TQRCodeGenLibGenericArray<IQrSegment>; static;
+
+  end;
+
+implementation
+
+{ TQrSegment }
+
+constructor TQrSegment.Create(AMode: TQrSegmentMode; ANumChars: Int32;
+  const AData: TQRCodeGenLibInt32Array; ABitLength: Int32);
+begin
+  Inherited Create();
+  FMode := AMode;
+  FData := AData;
+  if ((ANumChars < 0) or (ABitLength < 0) or
+    (ABitLength > (System.Length(AData) * Int64(32)))) then
+  begin
+    raise EArgumentInvalidQRCodeGenLibException.CreateRes(@SInvalidValue);
+  end;
+  FNumChars := ANumChars;
+  FBitLength := ABitLength;
+end;
+
+function TQrSegment.GetBitLength: Int32;
+begin
+  result := FBitLength;
+end;
+
+function TQrSegment.GetData: TQRCodeGenLibInt32Array;
+begin
+  result := System.Copy(FData);
+end;
+
+function TQrSegment.GetMode: TQrSegmentMode;
+begin
+  result := FMode;
+end;
+
+function TQrSegment.GetNumChars: Int32;
+begin
+  result := FNumChars;
+end;
+
+class function TQrSegment.GetTotalBits(const ASegments
+  : TQRCodeGenLibGenericArray<IQrSegment>; AVersion: Int32): Int32;
+var
+  LCCBits: Int32;
+  LResult: Int64;
+  LSegment: IQrSegment;
+begin
+  LResult := 0;
+  for LSegment in ASegments do
+  begin
+    TGuard.RequireNotNull(LSegment, SSegmentInstanceNil);
+    LCCBits := LSegment.Mode.NumCharCountBits(AVersion);
+    if (LSegment.NumChars >= (1 shl LCCBits)) then
+    begin
+      result := -1; // The segment's length doesn't fit the field's bit width
+      Exit;
+    end;
+    LResult := LResult + (Int64(4) + LCCBits + LSegment.BitLength);
+    if (LResult > System.High(Int32)) then
+    begin
+      result := -1; // The sum will overflow an Int32 type
+      Exit;
+    end;
+  end;
+
+  result := Int32(LResult);
+end;
+
+class function TQrSegment.IsAlphaNumeric(const AText: String): Boolean;
+var
+  LIdx, LBottom, LTop: Int32;
+  LChar: Char;
+begin
+{$IFDEF DELPHIXE3_UP}
+  LBottom := System.Low(AText);
+  LTop := System.High(AText);
+{$ELSE}
+  LBottom := 1;
+  LTop := System.Length(AText);
+{$ENDIF DELPHIXE3_UP}
+  for LIdx := LBottom to LTop do
+  begin
+    LChar := AText[LIdx];
+    if ((Ord(LChar) >= System.Length(FALPHANUMERIC_MAP)) or
+      (FALPHANUMERIC_MAP[Ord(LChar)] = -1)) then
+    begin
+      result := false;
+      Exit;
+    end;
+  end;
+  result := true;
+end;
+
+class function TQrSegment.IsNumeric(const AText: String): Boolean;
+var
+  LIdx, LBottom, LTop: Int32;
+  LChar: Char;
+begin
+{$IFDEF DELPHIXE3_UP}
+  LBottom := System.Low(AText);
+  LTop := System.High(AText);
+{$ELSE}
+  LBottom := 1;
+  LTop := System.Length(AText);
+{$ENDIF DELPHIXE3_UP}
+  for LIdx := LBottom to LTop do
+  begin
+    LChar := AText[LIdx];
+    if ((Ord(LChar) < Ord('0')) or (Ord(LChar) > Ord('9'))) then
+    begin
+      result := false;
+      Exit;
+    end;
+  end;
+  result := true;
+end;
+
+class function TQrSegment.MakeAlphaNumeric(const AText: String): IQrSegment;
+var
+  LBitBuffer: TBitBuffer;
+  LAccumData, LAccumCount, LIdx, LBottom, LTop: Int32;
+  LChar: Char;
+begin
+  LBitBuffer := TBitBuffer.Create();
+  LAccumData := 0;
+  LAccumCount := 0;
+{$IFDEF DELPHIXE3_UP}
+  LBottom := System.Low(AText);
+  LTop := System.High(AText);
+{$ELSE}
+  LBottom := 1;
+  LTop := System.Length(AText);
+{$ENDIF DELPHIXE3_UP}
+  for LIdx := LBottom to LTop do
+  begin
+    LChar := AText[LIdx];
+    if ((Ord(LChar) >= System.Length(FALPHANUMERIC_MAP)) or
+      (FALPHANUMERIC_MAP[Ord(LChar)] = -1)) then
+    begin
+      raise EArgumentInvalidQRCodeGenLibException.CreateRes
+        (@SUnEncodableCharacters);
+    end;
+    LAccumData := (LAccumData * 45) + FALPHANUMERIC_MAP[Ord(LChar)];
+    System.Inc(LAccumCount);
+    if (LAccumCount = 2) then
+    begin
+      LBitBuffer.AppendBits(LAccumData, 11);
+      LAccumData := 0;
+      LAccumCount := 0;
+    end;
+  end;
+  if (LAccumCount > 0) then // 1 character remaining
+  begin
+    LBitBuffer.AppendBits(LAccumData, 6);
+  end;
+  result := TQrSegment.Create(TQrSegmentMode.qsmAlphaNumeric,
+    System.Length(AText), LBitBuffer.Data, LBitBuffer.BitLength);
+end;
+
+class function TQrSegment.MakeBytes(const AData: TQRCodeGenLibByteArray)
+  : IQrSegment;
+var
+  LIdx: Int32;
+  LBits: TQRCodeGenLibInt32Array;
+begin
+  if ((System.Length(AData) * Int64(8)) > System.High(Int32)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes(@SDataTooLong);
+  end;
+  System.SetLength(LBits, (System.Length(AData) + 3) shr 2);
+  for LIdx := System.Low(AData) to System.High(AData) do
+  begin
+    LBits[TBits.Asr32(LIdx, 2)] := LBits[TBits.Asr32(LIdx, 2)] or
+      ((AData[LIdx] and $FF) shl ((not LIdx) shl 3));
+  end;
+  result := TQrSegment.Create(TQrSegmentMode.qsmByte, System.Length(AData),
+    LBits, System.Length(AData) * 8);
+end;
+
+class function TQrSegment.MakeEci(const AAssignValue: Int32): IQrSegment;
+var
+  LBitBuffer: TBitBuffer;
+begin
+  LBitBuffer := TBitBuffer.Create();
+  if (AAssignValue < 0) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SECIAssignmentOutOfRange);
+  end
+  else if (AAssignValue < (1 shl 7)) then
+  begin
+    LBitBuffer.AppendBits(AAssignValue, 8);
+  end
+  else if (AAssignValue < (1 shl 14)) then
+  begin
+    LBitBuffer.AppendBits(2, 2);
+    LBitBuffer.AppendBits(AAssignValue, 14);
+  end
+  else if (AAssignValue < 1000000) then
+  begin
+    LBitBuffer.AppendBits(6, 3);
+    LBitBuffer.AppendBits(AAssignValue, 21);
+  end
+  else
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SECIAssignmentOutOfRange);
+  end;
+  result := TQrSegment.Create(TQrSegmentMode.qsmEci, 0, LBitBuffer.Data,
+    LBitBuffer.BitLength);
+end;
+
+class function TQrSegment.MakeNumeric(const ADigits: String): IQrSegment;
+var
+  LBitBuffer: TBitBuffer;
+  LAccumData, LAccumCount, LIdx, LBottom, LTop: Int32;
+  LChar: Char;
+begin
+  LBitBuffer := TBitBuffer.Create();
+  LAccumData := 0;
+  LAccumCount := 0;
+{$IFDEF DELPHIXE3_UP}
+  LBottom := System.Low(ADigits);
+  LTop := System.High(ADigits);
+{$ELSE}
+  LBottom := 1;
+  LTop := System.Length(ADigits);
+{$ENDIF DELPHIXE3_UP}
+  for LIdx := LBottom to LTop do
+  begin
+    LChar := ADigits[LIdx];
+    if ((Ord(LChar) < Ord('0')) or (Ord(LChar) > Ord('9'))) then
+    begin
+      raise EArgumentInvalidQRCodeGenLibException.CreateRes
+        (@SNonNumericCharacters);
+    end;
+    LAccumData := (LAccumData * 10) + (Ord(LChar) - Ord('0'));
+    System.Inc(LAccumCount);
+    if (LAccumCount = 3) then
+    begin
+      LBitBuffer.AppendBits(LAccumData, 10);
+      LAccumData := 0;
+      LAccumCount := 0;
+    end;
+  end;
+  if (LAccumCount > 0) then // 1 or 2 digits remaining
+  begin
+    LBitBuffer.AppendBits(LAccumData, (LAccumCount * 3) + 1);
+  end;
+  result := TQrSegment.Create(TQrSegmentMode.qsmNumeric, System.Length(ADigits),
+    LBitBuffer.Data, LBitBuffer.BitLength);
+end;
+
+class function TQrSegment.MakeSegments(const AText: String;
+  const AEncoding: TEncoding): TQRCodeGenLibGenericArray<IQrSegment>;
+begin
+  TGuard.RequireNotNull(AEncoding, SEncodingInstanceNil);
+  // Select the most efficient segment encoding automatically
+  result := Nil;
+  if (AText <> '') then
+  begin
+    if IsNumeric(AText) then
+    begin
+      result := TQRCodeGenLibGenericArray<IQrSegment>.Create
+        (MakeNumeric(AText));
+      Exit;
+    end
+    else if IsAlphaNumeric(AText) then
+    begin
+      result := TQRCodeGenLibGenericArray<IQrSegment>.Create
+        (MakeAlphaNumeric(AText));
+      Exit;
+    end
+    else
+    begin
+      result := TQRCodeGenLibGenericArray<IQrSegment>.Create
+        (MakeBytes(TConverters.ConvertStringToBytes(AText, AEncoding)));
+      Exit;
+    end;
+  end;
+end;
+
+class constructor TQrSegment.QRSegment;
+var
+  LMaxChar, LIdx, LBottom, LTop, LFiller: Int32;
+begin
+  LFiller := -1;
+  LMaxChar := -1;
+{$IFDEF DELPHIXE3_UP}
+  LBottom := System.Low(ALPHANUMERIC_CHARSET);
+  LTop := System.High(ALPHANUMERIC_CHARSET);
+{$ELSE}
+  LBottom := 1;
+  LTop := System.Length(ALPHANUMERIC_CHARSET);
+{$ENDIF DELPHIXE3_UP}
+  for LIdx := LBottom to LTop do
+  begin
+    LMaxChar := Max(Ord(ALPHANUMERIC_CHARSET[LIdx]), LMaxChar);
+  end;
+  System.SetLength(FALPHANUMERIC_MAP, LMaxChar + 1);
+
+  TArrayUtils.Fill(FALPHANUMERIC_MAP, LFiller);
+
+  for LIdx := LBottom to LTop do
+  begin
+    FALPHANUMERIC_MAP[Ord(ALPHANUMERIC_CHARSET[LIdx])] := LIdx - 1;
+  end;
+end;
+
+end.

+ 110 - 0
QRCodeGenLib/src/QRCodeGen/QlpQrSegmentMode.pas

@@ -0,0 +1,110 @@
+unit QlpQrSegmentMode;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+{$IFDEF DEBUG}
+  QlpQrCodeCommons,
+{$ENDIF DEBUG}
+  QlpQRCodeGenLibTypes;
+
+resourcestring
+  SInvalidState = 'Invalid state encountered.';
+
+type
+{$SCOPEDENUMS ON}
+  /// <summary>
+  /// Describes how a segment's data bits are interpreted.
+  /// </summary>
+  TQrSegmentMode = (qsmNumeric = $1, qsmAlphaNumeric = $2, qsmByte = $4,
+    qsmKanji = $8, qsmEci = $7);
+{$SCOPEDENUMS OFF}
+
+type
+  TQrSegmentModeHelper = record helper for TQrSegmentMode
+  strict private
+    function GetModeBits(): Int32; inline;
+    function GetNumBitsCharCount(): TQRCodeGenLibInt32Array;
+
+  public
+
+    /// <summary>
+    /// Returns the bit width of the character count field for a segment in
+    /// this mode <br />in a QR Code at the given version number. The result
+    /// is in the range [0, 16].
+    /// </summary>
+    function NumCharCountBits(AVersion: Int32): Int32; inline;
+
+    /// <summary>
+    /// The mode indicator bits, which is a uint4 value (range 0 to 15).
+    /// </summary>
+    property ModeBits: Int32 read GetModeBits;
+
+    /// <summary>
+    /// Number of character count bits for three different version ranges.
+    /// </summary>
+    property NumBitsCharCount: TQRCodeGenLibInt32Array read GetNumBitsCharCount;
+  end;
+
+implementation
+
+{ TQrSegmentModeHelper }
+
+function TQrSegmentModeHelper.GetModeBits: Int32;
+begin
+  result := Ord(Self);
+end;
+
+function TQrSegmentModeHelper.GetNumBitsCharCount: TQRCodeGenLibInt32Array;
+begin
+  case Self of
+
+    TQrSegmentMode.qsmNumeric:
+      begin
+        result := TQRCodeGenLibInt32Array.Create(10, 12, 14);
+        Exit;
+      end;
+
+    TQrSegmentMode.qsmAlphaNumeric:
+      begin
+        result := TQRCodeGenLibInt32Array.Create(9, 11, 13);
+        Exit;
+      end;
+
+    TQrSegmentMode.qsmByte:
+      begin
+        result := TQRCodeGenLibInt32Array.Create(8, 16, 16);
+        Exit;
+      end;
+
+    TQrSegmentMode.qsmKanji:
+      begin
+        result := TQRCodeGenLibInt32Array.Create(8, 10, 12);
+        Exit;
+      end;
+
+    TQrSegmentMode.qsmEci:
+      begin
+        result := TQRCodeGenLibInt32Array.Create(0, 0, 0);
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationQRCodeGenLibException.CreateRes(@SInvalidState);
+    end;
+
+  end;
+end;
+
+function TQrSegmentModeHelper.NumCharCountBits(AVersion: Int32): Int32;
+begin
+{$IFDEF DEBUG}
+  System.Assert((TQrCodeCommons.MIN_VERSION <= AVersion) and
+    (AVersion <= TQrCodeCommons.MAX_VERSION));
+{$ENDIF DEBUG}
+  result := NumBitsCharCount[(AVersion + 7) div 17];
+end;
+
+end.

+ 555 - 0
QRCodeGenLib/src/QRCodeGen/QlpQrTemplate.pas

@@ -0,0 +1,555 @@
+unit QlpQrTemplate;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  Math,
+  SyncObjs,
+  QlpIQrTemplate,
+  QlpQrCodeCommons,
+  QlpBits,
+  QlpQRCodeGenLibTypes;
+
+resourcestring
+  SVersionOutOfRange = 'Version out of range';
+  SVersionNumberOutOfRange = 'Version number out of range';
+  SInvalidState = 'Invalid state encountered.';
+
+type
+  TQrTemplate = class sealed(TInterfacedObject, IQrTemplate)
+  strict private
+  const
+    MIN_VERSION = TQrCodeCommons.MIN_VERSION;
+    MAX_VERSION = TQrCodeCommons.MAX_VERSION;
+
+  class var
+    FCache: array [0 .. MAX_VERSION + 1] of IQrTemplate;
+    FIsPending: array [0 .. MAX_VERSION + 1] of Boolean;
+    FLock: TCriticalSection;
+
+  var
+    FVersion, FSize: Int32;
+
+    // "FIsFunction" is Discarded when constructor finishes
+    FTemplate, FDataOutputBitIndexes, FIsFunction: TQRCodeGenLibInt32Array;
+    FMasks: TQRCodeGenLibMatrixInt32Array;
+
+    function GetTemplate(): TQRCodeGenLibInt32Array; inline;
+    function GetDataOutputBitIndexes(): TQRCodeGenLibInt32Array; inline;
+    function GetMasks(): TQRCodeGenLibMatrixInt32Array;
+
+    function GetModule(const AGrid: TQRCodeGenLibInt32Array; Ax, Ay: Int32)
+      : Int32; inline;
+
+    // Returns an ascending list of positions of alignment patterns for this version number.
+    // Each position is in the range [0,177), and are used on both the x and y axes.
+    // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
+    function GetAlignmentPatternPositions(): TQRCodeGenLibInt32Array;
+
+    function GenerateZigZagScan(): TQRCodeGenLibInt32Array;
+
+    function GenerateMasks(): TQRCodeGenLibMatrixInt32Array;
+
+    procedure DrawFunctionPatterns();
+    // Draws two blank copies of the format bits.
+    procedure DrawDummyFormatBits();
+    // Draws two copies of the version bits (with its own error correction code),
+    // based on this object's version field, iff 7 <= version <= 40.
+    procedure DrawVersion();
+    // Draws a 9*9 finder pattern including the border separator,
+    // with the center module at (Ax, Ay). Modules can be out of bounds.
+    procedure DrawFinderPattern(Ax, Ay: Int32);
+    // Draws a 5*5 alignment pattern, with the center module
+    // at (Ax, Ay). All modules must be in bounds.
+    procedure DrawAlignmentPattern(Ax, Ay: Int32);
+    // Marks the module at the given coordinates as a function module.
+    // Also either sets that module black or keeps its color unchanged.
+    procedure DarkenFunctionModule(Ax, Ay, AEnable: Int32); inline;
+
+    constructor Create(AVersion: Int32);
+
+    class constructor CreateQrTemplate();
+    class destructor DestroyQrTemplate();
+
+  public
+
+    property Template: TQRCodeGenLibInt32Array read GetTemplate;
+    property DataOutputBitIndexes: TQRCodeGenLibInt32Array
+      read GetDataOutputBitIndexes;
+    property Masks: TQRCodeGenLibMatrixInt32Array read GetMasks;
+
+    class function GetInstance(AVersion: Int32): IQrTemplate; static;
+    // Returns the number of data bits that can be stored in a QR Code of the given version number, after
+    // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
+    // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
+    class function GetNumRawDataModules(AVersion: Int32): Int32; static;
+
+  end;
+
+implementation
+
+{ TQrTemplate }
+
+constructor TQrTemplate.Create(AVersion: Int32);
+begin
+  Inherited Create();
+  if ((AVersion < MIN_VERSION) or (AVersion > MAX_VERSION)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SVersionOutOfRange);
+  end;
+  FVersion := AVersion;
+  FSize := (AVersion * 4) + 17;
+  System.SetLength(FTemplate, ((FSize * FSize) + 31) shr 5);
+  System.SetLength(FIsFunction, System.Length(FTemplate));
+
+  DrawFunctionPatterns(); // Reads and writes fields
+  FMasks := GenerateMasks(); // Reads fields, returns array
+  FDataOutputBitIndexes := GenerateZigZagScan(); // Reads fields, returns array
+  FIsFunction := Nil;
+end;
+
+class constructor TQrTemplate.CreateQrTemplate;
+var
+  LIdx: Int32;
+begin
+  // Initialize static array to their default state to avoid junk values inside.
+  for LIdx := System.Low(FCache) to System.High(FCache) do
+  begin
+    FCache[LIdx] := Nil;
+    FIsPending[LIdx] := False;
+  end;
+  FLock := TCriticalSection.Create;
+end;
+
+function TQrTemplate.GetModule(const AGrid: TQRCodeGenLibInt32Array;
+  Ax, Ay: Int32): Int32;
+var
+  LIdx: Int32;
+begin
+{$IFDEF DEBUG}
+  System.Assert((0 <= Ax) and (Ax < FSize));
+  System.Assert((0 <= Ay) and (Ay < FSize));
+{$ENDIF DEBUG}
+  LIdx := (Ay * FSize) + Ax;
+  result := TQrCodeCommons.GetBit(AGrid[TBits.Asr32(LIdx, 5)], LIdx);
+end;
+
+class function TQrTemplate.GetNumRawDataModules(AVersion: Int32): Int32;
+var
+  numAlign: Int32;
+begin
+  if ((AVersion < MIN_VERSION) or (AVersion > MAX_VERSION)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SVersionNumberOutOfRange);
+  end;
+  result := (((16 * AVersion) + 128) * AVersion) + 64;
+  if (AVersion >= 2) then
+  begin
+    numAlign := (AVersion div 7) + 2;
+    result := result - ((((25 * numAlign) - 10) * numAlign) - 55);
+    if (AVersion >= 7) then
+    begin
+      result := result - 36;
+    end;
+  end;
+end;
+
+procedure TQrTemplate.DarkenFunctionModule(Ax, Ay, AEnable: Int32);
+var
+  LIdx: Int32;
+begin
+{$IFDEF DEBUG}
+  System.Assert((0 <= Ax) and (Ax < FSize));
+  System.Assert((0 <= Ay) and (Ay < FSize));
+  System.Assert((AEnable = 0) or (AEnable = 1));
+{$ENDIF DEBUG}
+  LIdx := (Ay * FSize) + Ax;
+  FTemplate[TBits.Asr32(LIdx, 5)] := FTemplate[TBits.Asr32(LIdx, 5)] or
+    (AEnable shl LIdx);
+  FIsFunction[TBits.Asr32(LIdx, 5)] := FIsFunction[TBits.Asr32(LIdx, 5)] or
+    (1 shl LIdx);
+end;
+
+class destructor TQrTemplate.DestroyQrTemplate;
+var
+  LIdx: Int32;
+begin
+  // Initialize static array to their default state to clear former contents.
+  for LIdx := System.Low(FCache) to System.High(FCache) do
+  begin
+    FCache[LIdx] := Nil;
+    FIsPending[LIdx] := False;
+  end;
+  FLock.Free;
+end;
+
+procedure TQrTemplate.DrawAlignmentPattern(Ax, Ay: Int32);
+var
+  Ldy, Ldx: Int32;
+begin
+  for Ldy := -2 to 2 do
+  begin
+    for Ldx := -2 to 2 do
+    begin
+      DarkenFunctionModule(Ax + Ldx, Ay + Ldy,
+        Abs(Max(Abs(Ldx), Abs(Ldy)) - 1));
+    end;
+  end;
+end;
+
+procedure TQrTemplate.DrawDummyFormatBits;
+var
+  LIdx: Int32;
+begin
+  // Draw first copy
+  for LIdx := 0 to 5 do
+  begin
+    DarkenFunctionModule(8, LIdx, 0);
+  end;
+  DarkenFunctionModule(8, 7, 0);
+  DarkenFunctionModule(8, 8, 0);
+  DarkenFunctionModule(7, 8, 0);
+
+  for LIdx := 9 to System.Pred(15) do
+  begin
+    DarkenFunctionModule(14 - LIdx, 8, 0);
+  end;
+
+  // Draw second copy
+  for LIdx := 0 to 7 do
+  begin
+    DarkenFunctionModule(FSize - 1 - LIdx, 8, 0);
+  end;
+
+  for LIdx := 8 to System.Pred(15) do
+  begin
+    DarkenFunctionModule(8, FSize - 15 + LIdx, 0);
+  end;
+  DarkenFunctionModule(8, FSize - 8, 1);
+end;
+
+procedure TQrTemplate.DrawFinderPattern(Ax, Ay: Int32);
+var
+  Ldy, Ldx, LDist, Lxx, Lyy, LEnable: Int32;
+begin
+  for Ldy := -4 to 4 do
+  begin
+    for Ldx := -4 to 4 do
+    begin
+      LDist := Max(Abs(Ldy), Abs(Ldx)); // Chebyshev/infinity norm
+      Lxx := Ax + Ldx;
+      Lyy := Ay + Ldy;
+      if ((0 <= Lxx) and (Lxx < FSize) and (0 <= Lyy) and (Lyy < FSize)) then
+      begin
+        if ((LDist <> 2) and (LDist <> 4)) then
+        begin
+          LEnable := 1;
+        end
+        else
+        begin
+          LEnable := 0;
+        end;
+        DarkenFunctionModule(Lxx, Lyy, LEnable);
+      end;
+    end;
+  end;
+end;
+
+procedure TQrTemplate.DrawFunctionPatterns;
+var
+  LIIdx, LJIdx, LNumAlign: Int32;
+  LAlignPatPos: TQRCodeGenLibInt32Array;
+begin
+  // Draw horizontal and vertical timing patterns
+  for LIIdx := 0 to System.Pred(FSize) do
+  begin
+    DarkenFunctionModule(6, LIIdx, (not LIIdx) and 1);
+    DarkenFunctionModule(LIIdx, 6, (not LIIdx) and 1);
+  end;
+
+  // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
+  DrawFinderPattern(3, 3);
+  DrawFinderPattern(FSize - 4, 3);
+  DrawFinderPattern(3, FSize - 4);
+
+  // Draw numerous alignment patterns
+  LAlignPatPos := GetAlignmentPatternPositions();
+  LNumAlign := System.Length(LAlignPatPos);
+  for LIIdx := 0 to System.Pred(LNumAlign) do
+  begin
+    for LJIdx := 0 to System.Pred(LNumAlign) do
+    begin
+      if (not(((LIIdx = 0) and (LJIdx = 0)) or ((LIIdx = 0) and
+        (LJIdx = LNumAlign - 1)) or ((LIIdx = LNumAlign - 1) and (LJIdx = 0))))
+      then
+      begin
+        DrawAlignmentPattern(LAlignPatPos[LIIdx], LAlignPatPos[LJIdx]);
+      end;
+    end;
+  end;
+
+  // Draw configuration data
+  DrawDummyFormatBits();
+  DrawVersion();
+end;
+
+procedure TQrTemplate.DrawVersion;
+var
+  LRem, LIdx, LBit, LBits, La, Lb: Int32;
+begin
+  if (FVersion < 7) then
+  begin
+    Exit;
+  end;
+
+  // Calculate error correction code and pack bits
+  LRem := FVersion; // version is uint6, in the range [7, 40]
+  LIdx := 0;
+  while LIdx < 12 do
+  begin
+    LRem := (LRem shl 1) xor ((TBits.Asr32(LRem, 11)) * $1F25);
+    System.Inc(LIdx);
+  end;
+  LBits := (FVersion shl 12) or LRem; // uint18
+{$IFDEF DEBUG}
+  System.Assert(TBits.Asr32(LBits, 18) = 0);
+{$ENDIF DEBUG}
+  // Draw two copies
+  for LIdx := 0 to System.Pred(18) do
+  begin
+    LBit := TQrCodeCommons.GetBit(LBits, LIdx);
+    La := FSize - 11 + (LIdx mod 3);
+    Lb := LIdx div 3;
+    DarkenFunctionModule(La, Lb, LBit);
+    DarkenFunctionModule(Lb, La, LBit);
+  end;
+end;
+
+function TQrTemplate.GenerateMasks: TQRCodeGenLibMatrixInt32Array;
+var
+  LMask, Ly, LIdx, Lx, LBit: Int32;
+  LInvert: Boolean;
+  LMaskModules: TQRCodeGenLibInt32Array;
+begin
+  System.SetLength(result, 8);
+  for LMask := System.Low(result) to System.High(result) do
+  begin
+    // resize dimension of inner array
+    System.SetLength(result[LMask], System.Length(FTemplate));
+    LMaskModules := result[LMask];
+    Ly := 0;
+    LIdx := 0;
+    while Ly < FSize do
+    begin
+      Lx := 0;
+      while Lx < FSize do
+      begin
+        case LMask of
+          0:
+            begin
+              LInvert := (Lx + Ly) and 1 = 0;
+            end;
+          1:
+            begin
+              LInvert := Ly and 1 = 0;
+            end;
+          2:
+            begin
+              LInvert := Lx mod 3 = 0;
+            end;
+          3:
+            begin
+              LInvert := (Lx + Ly) mod 3 = 0;
+            end;
+          4:
+            begin
+              LInvert := ((Lx div 3) + (Ly shr 1)) and 1 = 0;
+            end;
+          5:
+            begin
+              LInvert := ((Lx * Ly) and 1) + Lx * Ly mod 3 = 0;
+            end;
+          6:
+            begin
+              LInvert := (((Lx * Ly) and 1) + Lx * Ly mod 3) and 1 = 0;
+            end;
+          7:
+            begin
+              LInvert := (((Lx + Ly) and 1) + Lx * Ly mod 3) and 1 = 0;
+            end
+        else
+          begin
+            raise EInvalidOperationQRCodeGenLibException.CreateRes
+              (@SInvalidState);
+          end;
+
+        end;
+
+        if LInvert then
+        begin
+          LBit := 1 and (not GetModule(FIsFunction, Lx, Ly));
+        end
+        else
+        begin
+          LBit := 0 and (not GetModule(FIsFunction, Lx, Ly));
+        end;
+
+        LMaskModules[TBits.Asr32(LIdx, 5)] := LMaskModules[TBits.Asr32(LIdx, 5)
+          ] or (LBit shl LIdx);
+        System.Inc(Lx);
+        System.Inc(LIdx);
+      end;
+      System.Inc(Ly);
+    end;
+  end;
+end;
+
+function TQrTemplate.GenerateZigZagScan: TQRCodeGenLibInt32Array;
+var
+  LIIdx, LJIdx, LRight, LVert, Lx, Ly: Int32;
+  LUpward: Boolean;
+begin
+  System.SetLength(result, ((GetNumRawDataModules(FVersion) div 8) * 8));
+  LIIdx := 0; // Bit index into the data
+  LRight := FSize - 1;
+  while LRight >= 1 do
+  begin // Index of right column in each column pair
+    if (LRight = 6) then
+    begin
+      LRight := 5;
+    end;
+    LVert := 0;
+    while LVert < FSize do
+    begin
+      // Vertical counter
+      LJIdx := 0;
+      while LJIdx < 2 do
+      begin
+        Lx := LRight - LJIdx; // Actual x coordinate
+        LUpward := ((LRight + 1) and 2) = 0;
+        // Actual y coordinate
+        if LUpward then
+        begin
+          Ly := FSize - 1 - LVert;
+        end
+        else
+        begin
+          Ly := LVert;
+        end;
+
+        if ((GetModule(FIsFunction, Lx, Ly) = 0) and
+          (LIIdx < System.Length(result))) then
+        begin
+          result[LIIdx] := (Ly * FSize) + Lx;
+          System.Inc(LIIdx);
+        end;
+        System.Inc(LJIdx);
+      end;
+      System.Inc(LVert);
+    end;
+    System.Dec(LRight, 2);
+  end;
+{$IFDEF DEBUG}
+  System.Assert(LIIdx = System.Length(result));
+{$ENDIF DEBUG}
+end;
+
+function TQrTemplate.GetAlignmentPatternPositions: TQRCodeGenLibInt32Array;
+var
+  LNumAlign, LStep, LIdx, LPos: Int32;
+begin
+  if (FVersion = 1) then
+  begin
+    result := Nil;
+    Exit;
+  end
+  else
+  begin
+    LNumAlign := (FVersion div 7) + 2;
+    if (FVersion = 32) then
+    begin
+      LStep := 26;
+    end
+    else
+    begin
+      LStep := (((FVersion * 4) + (LNumAlign * 2) + 1)
+        div ((LNumAlign * 2) - 2)) * 2;
+    end;
+    System.SetLength(result, LNumAlign);
+    result[0] := 6;
+    LIdx := System.Length(result) - 1;
+    LPos := FSize - 7;
+    while LIdx >= 1 do
+    begin
+      result[LIdx] := LPos;
+      System.Dec(LIdx);
+      System.Dec(LPos, LStep);
+    end;
+  end;
+end;
+
+function TQrTemplate.GetDataOutputBitIndexes: TQRCodeGenLibInt32Array;
+begin
+  result := System.Copy(FDataOutputBitIndexes);
+end;
+
+class function TQrTemplate.GetInstance(AVersion: Int32): IQrTemplate;
+begin
+  if ((AVersion < MIN_VERSION) or (AVersion > MAX_VERSION)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SVersionOutOfRange);
+  end;
+
+  while True do
+  begin
+    FLock.Acquire;
+    try
+      result := FCache[AVersion];
+      if result <> Nil then
+      begin
+        Exit;
+      end;
+
+      if (not(FIsPending[AVersion])) then
+      begin
+        FIsPending[AVersion] := True;
+        Break;
+      end;
+    finally
+      FLock.Release;
+    end;
+  end;
+
+  result := TQrTemplate.Create(AVersion);
+  FLock.Acquire;
+  try
+    FCache[AVersion] := result;
+    FIsPending[AVersion] := False;
+  finally
+    FLock.Release;
+  end;
+end;
+
+function TQrTemplate.GetMasks: TQRCodeGenLibMatrixInt32Array;
+var
+  LIdx: Int32;
+begin
+  // since System.Copy() does not support jagged arrays (multidimensional dynamic arrays, we improvise)
+  System.SetLength(result, System.Length(FMasks));
+  for LIdx := System.Low(result) to System.High(result) do
+  begin
+    result[LIdx] := System.Copy(FMasks[LIdx]);
+  end;
+end;
+
+function TQrTemplate.GetTemplate: TQRCodeGenLibInt32Array;
+begin
+  result := System.Copy(FTemplate);
+end;
+
+end.

+ 225 - 0
QRCodeGenLib/src/QRCodeGen/QlpReedSolomonGenerator.pas

@@ -0,0 +1,225 @@
+unit QlpReedSolomonGenerator;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  SyncObjs,
+  QlpIReedSolomonGenerator,
+  QlpBits,
+  QlpArrayUtils,
+  QlpQRCodeGenLibTypes;
+
+resourcestring
+  SDegreeOutOfRange = 'Degree out of range';
+
+type
+  TReedSolomonGenerator = class sealed(TInterfacedObject, IReedSolomonGenerator)
+
+  strict private
+  const
+    MAX_DEGREE = Int32(30);
+
+  class var
+    FCache: array [0 .. MAX_DEGREE + 1] of IReedSolomonGenerator;
+    FIsPending: array [0 .. MAX_DEGREE + 1] of Boolean;
+    FLock: TCriticalSection;
+
+  var
+    // A table of size 256 * degree, where FPolynomialMultiply[i][j] := Multiply(i, coefficients[j]).
+    // 'coefficients' is the temporary array representing the coefficients of the divisor polynomial,
+    // stored from highest to lowest power, excluding the leading term which is always 1.
+    // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
+    FPolynomialMultiply: TQRCodeGenLibMatrixByteArray;
+
+    constructor Create(ADegree: Int32);
+    // Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result
+    // are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.
+    class function Multiply(Ax, Ay: Int32): Int32; static;
+
+    class constructor CreateReedSolomonGenerator();
+    class destructor DestroyReedSolomonGenerator();
+
+  public
+    procedure GetRemainder(const AData: TQRCodeGenLibByteArray;
+      ADataOff, ADataLen: Int32; const AResult: TQRCodeGenLibByteArray);
+    class function GetInstance(ADegree: Int32): IReedSolomonGenerator; static;
+
+  end;
+
+implementation
+
+{ TReedSolomonGenerator }
+
+constructor TReedSolomonGenerator.Create(ADegree: Int32);
+var
+  LCoefficients: TQRCodeGenLibByteArray;
+  LRoot, LIIdx, LJIdx: Int32;
+begin
+  Inherited Create();
+  if ((ADegree < 1) or (ADegree > 255)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SDegreeOutOfRange);
+  end;
+
+  // Start with the monomial x^0
+  System.SetLength(LCoefficients, ADegree);
+  LCoefficients[ADegree - 1] := 1;
+
+  // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
+  // drop the highest term, and store the rest of the coefficients in order of descending powers.
+  // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
+  LRoot := 1;
+  LIIdx := 0;
+  while LIIdx < ADegree do
+  begin
+    // Multiply the current product by (x - r^i)
+    for LJIdx := System.Low(LCoefficients) to System.High(LCoefficients) do
+    begin
+      LCoefficients[LJIdx] :=
+        Byte(Multiply(LCoefficients[LJIdx] and $FF, LRoot));
+      if ((LJIdx + 1) < System.Length(LCoefficients)) then
+      begin
+        LCoefficients[LJIdx] := LCoefficients[LJIdx]
+          xor (LCoefficients[LJIdx + 1]);
+      end;
+    end;
+    LRoot := Multiply(LRoot, $02);
+    System.Inc(LIIdx);
+  end;
+
+  System.SetLength(FPolynomialMultiply, 256);
+
+  for LIIdx := System.Low(FPolynomialMultiply)
+    to System.High(FPolynomialMultiply) do
+  begin
+    // resize dimension of inner array
+    System.SetLength(FPolynomialMultiply[LIIdx], ADegree);
+    for LJIdx := 0 to System.Pred(ADegree) do
+    begin
+      FPolynomialMultiply[LIIdx][LJIdx] :=
+        Byte(Multiply(LIIdx, LCoefficients[LJIdx] and $FF));
+    end;
+  end;
+end;
+
+class constructor TReedSolomonGenerator.CreateReedSolomonGenerator;
+var
+  LIdx: Int32;
+begin
+  // Initialize static array to their default state to avoid junk values inside.
+  for LIdx := System.Low(FCache) to System.High(FCache) do
+  begin
+    FCache[LIdx] := Nil;
+    FIsPending[LIdx] := False;
+  end;
+  FLock := TCriticalSection.Create;
+end;
+
+class destructor TReedSolomonGenerator.DestroyReedSolomonGenerator;
+var
+  LIdx: Int32;
+begin
+  // Initialize static array to their default state to clear former contents.
+  for LIdx := System.Low(FCache) to System.High(FCache) do
+  begin
+    FCache[LIdx] := Nil;
+    FIsPending[LIdx] := False;
+  end;
+  FLock.Free;
+end;
+
+class function TReedSolomonGenerator.GetInstance(ADegree: Int32)
+  : IReedSolomonGenerator;
+begin
+  if ((ADegree < 1) or (ADegree > MAX_DEGREE)) then
+  begin
+    raise EArgumentOutOfRangeQRCodeGenLibException.CreateRes
+      (@SDegreeOutOfRange);
+  end;
+
+  while True do
+  begin
+    FLock.Acquire;
+    try
+      result := FCache[ADegree];
+      if result <> Nil then
+      begin
+        Exit;
+      end;
+
+      if (not(FIsPending[ADegree])) then
+      begin
+        FIsPending[ADegree] := True;
+        break;
+      end;
+    finally
+      FLock.Release;
+    end;
+  end;
+
+  result := TReedSolomonGenerator.Create(ADegree);
+  FLock.Acquire;
+  try
+    FCache[ADegree] := result;
+    FIsPending[ADegree] := False;
+  finally
+    FLock.Release;
+  end;
+end;
+
+procedure TReedSolomonGenerator.GetRemainder(const AData
+  : TQRCodeGenLibByteArray; ADataOff, ADataLen: Int32;
+  const AResult: TQRCodeGenLibByteArray);
+var
+  LDegree, LIIdx, LJIdx, LDataEnd: Int32;
+  LTable: TQRCodeGenLibByteArray;
+begin
+  LDegree := System.Length(FPolynomialMultiply[0]);
+{$IFDEF DEBUG}
+  System.Assert(System.Length(AResult) = LDegree);
+{$ENDIF DEBUG}
+  // Compute the remainder by performing polynomial division
+  TArrayUtils.Fill(AResult, Byte(0));
+  LIIdx := ADataOff;
+  LDataEnd := ADataOff + ADataLen;
+  while LIIdx < LDataEnd do
+  begin
+    LTable := FPolynomialMultiply[(AData[LIIdx] xor AResult[0]) and $FF];
+    LJIdx := 0;
+    while LJIdx < (System.Pred(LDegree)) do
+    begin
+      AResult[LJIdx] := Byte(AResult[LJIdx + 1] xor LTable[LJIdx]);
+      System.Inc(LJIdx);
+    end;
+    AResult[LDegree - 1] := LTable[LDegree - 1];
+    System.Inc(LIIdx);
+  end;
+
+end;
+
+class function TReedSolomonGenerator.Multiply(Ax, Ay: Int32): Int32;
+var
+  Lz, LIdx: Int32;
+begin
+{$IFDEF DEBUG}
+  System.Assert((TBits.Asr32(Ax, 8) = 0) and (TBits.Asr32(Ay, 8) = 0));
+{$ENDIF DEBUG}
+  // Russian peasant multiplication
+  Lz := 0;
+  LIdx := 7;
+  while LIdx >= 0 do
+  begin
+    Lz := (Lz shl 1) xor ((TBits.Asr32(Lz, 7)) * $11D);
+    Lz := Lz xor (((TBits.Asr32(Ay, LIdx)) and 1) * Ax);
+    System.Dec(LIdx);
+  end;
+{$IFDEF DEBUG}
+  System.Assert((TBits.Asr32(Lz, 8) = 0));
+{$ENDIF DEBUG}
+  result := Lz;
+end;
+
+end.

+ 92 - 0
QRCodeGenLib/src/Utils/QlpArrayUtils.pas

@@ -0,0 +1,92 @@
+unit QlpArrayUtils;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  Math,
+  QlpQRCodeGenLibTypes;
+
+type
+  TArrayUtils = class sealed(TObject)
+
+  public
+
+    /// <summary>
+    /// Copies the specified array, truncating or padding with zeros (if
+    /// necessary) so the copy has the specified length. For all indices that
+    /// are valid in both the original array and the copy, the two arrays
+    /// will contain identical values. For any indices that are valid in the
+    /// copy but not the original, the copy will contain <c>0</c>. <br />Such
+    /// indices will exist if and only if the specified length is greater
+    /// than that of the original array.
+    /// </summary>
+    /// <param name="ANewLength">
+    /// the length of the copy to be returned
+    /// </param>
+    /// <param name="AData">
+    /// the array to be copied
+    /// </param>
+    /// <remarks>
+    /// This Function Assumes <c>AOriginal</c> and <c>ANewLength</c> are
+    /// Valid. (Non Null and Non Negative Respectively)
+    /// </remarks>
+    class function CopyOf(const AOriginal: TQRCodeGenLibInt32Array;
+      ANewLength: Int32): TQRCodeGenLibInt32Array; static; inline;
+
+    /// <summary>
+    /// Assigns the specified <c>Int32</c> value to each element of the
+    /// specified array of <c>Int32s</c>.
+    /// </summary>
+    /// <param name="ABuffer">
+    /// the array to be filled
+    /// </param>
+    /// <param name="AFiller">
+    /// the value to be stored in all elements of the array
+    /// </param>
+    class procedure Fill(const ABuffer: TQRCodeGenLibInt32Array;
+      AFiller: Int32); overload; static; inline;
+
+    /// <summary>
+    /// Assigns the specified <c>Byte</c> value to each element of the
+    /// specified array of <c>Bytes</c>.
+    /// </summary>
+    /// <param name="ABuffer">
+    /// the array to be filled
+    /// </param>
+    /// <param name="AFiller">
+    /// the value to be stored in all elements of the array
+    /// </param>
+    class procedure Fill(const ABuffer: TQRCodeGenLibByteArray; AFiller: Byte);
+      overload; static; inline;
+
+  end;
+
+implementation
+
+{ TArrayUtils }
+
+class function TArrayUtils.CopyOf(const AOriginal: TQRCodeGenLibInt32Array;
+  ANewLength: Int32): TQRCodeGenLibInt32Array;
+begin
+  System.SetLength(Result, ANewLength);
+  System.Move(AOriginal[0], Result[0], Min(System.Length(AOriginal), ANewLength)
+    * System.SizeOf(Int32));
+end;
+
+class procedure TArrayUtils.Fill(const ABuffer: TQRCodeGenLibInt32Array;
+  AFiller: Int32);
+begin
+  System.FillChar(ABuffer[0], System.Length(ABuffer) *
+    System.SizeOf(Int32), AFiller);
+end;
+
+class procedure TArrayUtils.Fill(const ABuffer: TQRCodeGenLibByteArray;
+  AFiller: Byte);
+begin
+  System.FillChar(ABuffer[0], System.Length(ABuffer) *
+    System.SizeOf(Byte), AFiller);
+end;
+
+end.

+ 69 - 0
QRCodeGenLib/src/Utils/QlpBits.pas

@@ -0,0 +1,69 @@
+unit QlpBits;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+type
+  TBits = class sealed(TObject)
+
+  public
+
+    /// <summary>
+    /// Calculates Arithmetic shift right.
+    /// </summary>
+    /// <param name="AValue">Int32 value to compute 'Asr' on.</param>
+    /// <param name="AShiftBits">Byte, number of bits to shift value to.</param>
+    /// <returns>Shifted value.</returns>
+    /// <remarks>
+    /// Emulated Implementation was gotten from FreePascal sources
+    /// </remarks>
+
+    class function Asr32(AValue: Int32; AShiftBits: Byte): Int32;
+      static; inline;
+
+    /// <summary>
+    /// Calculates Arithmetic shift right.
+    /// </summary>
+    /// <param name="AValue">Int64 value to compute 'Asr' on.</param>
+    /// <param name="AShiftBits">Byte, number of bits to shift value to.</param>
+    /// <returns>Shifted value.</returns>
+    /// <remarks>
+    /// Emulated Implementation was gotten from FreePascal sources
+    /// </remarks>
+
+    class function Asr64(AValue: Int64; AShiftBits: Byte): Int64;
+      static; inline;
+
+  end;
+
+implementation
+
+{ TBits }
+
+class function TBits.Asr32(AValue: Int32; AShiftBits: Byte): Int32;
+
+begin
+{$IFDEF FPC}
+  Result := SarLongInt(AValue, AShiftBits);
+{$ELSE}
+  Result := Int32(UInt32(UInt32(UInt32(AValue) shr (AShiftBits and 31)) or
+    (UInt32(Int32(UInt32(0 - UInt32(UInt32(AValue) shr 31)) and
+    UInt32(Int32(0 - (Ord((AShiftBits and 31) <> 0) { and 1 } )))))
+    shl (32 - (AShiftBits and 31)))));
+{$ENDIF FPC}
+end;
+
+class function TBits.Asr64(AValue: Int64; AShiftBits: Byte): Int64;
+begin
+{$IFDEF FPC}
+  Result := SarInt64(AValue, AShiftBits);
+{$ELSE}
+  Result := Int64(UInt64(UInt64(UInt64(AValue) shr (AShiftBits and 63)) or
+    (UInt64(Int64(UInt64(0 - UInt64(UInt64(AValue) shr 63)) and
+    UInt64(Int64(0 - (Ord((AShiftBits and 63) <> 0) { and 1 } )))))
+    shl (64 - (AShiftBits and 63)))));
+{$ENDIF FPC}
+end;
+
+end.

+ 53 - 0
QRCodeGenLib/src/Utils/QlpConverters.pas

@@ -0,0 +1,53 @@
+unit QlpConverters;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  QlpGuard,
+  QlpQRCodeGenLibTypes;
+
+resourcestring
+  SEncodingInstanceNil = 'Encoding instance cannot be nil';
+
+type
+  TConverters = class sealed(TObject)
+
+  public
+
+    class function ConvertStringToBytes(const AInput: String;
+      const AEncoding: TEncoding): TQRCodeGenLibByteArray; overload; static;
+
+    class function ConvertBytesToString(const AInput: TQRCodeGenLibByteArray;
+      const AEncoding: TEncoding): String; overload; static;
+  end;
+
+implementation
+
+{ TConverters }
+
+class function TConverters.ConvertStringToBytes(const AInput: String;
+  const AEncoding: TEncoding): TQRCodeGenLibByteArray;
+begin
+  TGuard.RequireNotNull(AEncoding, SEncodingInstanceNil);
+{$IFDEF FPC}
+  result := AEncoding.GetBytes(UnicodeString(AInput));
+{$ELSE}
+  result := AEncoding.GetBytes(AInput);
+{$ENDIF FPC}
+end;
+
+class function TConverters.ConvertBytesToString(const AInput
+  : TQRCodeGenLibByteArray; const AEncoding: TEncoding): String;
+begin
+  TGuard.RequireNotNull(AEncoding, SEncodingInstanceNil);
+{$IFDEF FPC}
+  result := String(AEncoding.GetString(AInput));
+{$ELSE}
+  result := AEncoding.GetString(AInput);
+{$ENDIF FPC}
+end;
+
+end.

+ 42 - 0
QRCodeGenLib/src/Utils/QlpGuard.pas

@@ -0,0 +1,42 @@
+unit QlpGuard;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  QlpQRCodeGenLibTypes;
+
+type
+  TGuard = class sealed(TObject)
+
+  public
+    class procedure RequireNotNull(const AObject: TObject;
+      const AMessage: String = ''); overload; static;
+    class procedure RequireNotNull(const AInterface: IInterface;
+      const AMessage: String = ''); overload; static;
+  end;
+
+implementation
+
+{ TGuard }
+
+class procedure TGuard.RequireNotNull(const AObject: TObject;
+  const AMessage: String);
+begin
+  if AObject = Nil then
+  begin
+    raise ENullReferenceQRCodeGenLibException.Create(AMessage);
+  end;
+end;
+
+class procedure TGuard.RequireNotNull(const AInterface: IInterface;
+  const AMessage: String);
+begin
+  if AInterface = Nil then
+  begin
+    raise ENullReferenceQRCodeGenLibException.Create(AMessage);
+  end;
+end;
+
+end.

+ 98 - 0
QRCodeGenLib/src/Utils/QlpQRCodeGenLibTypes.pas

@@ -0,0 +1,98 @@
+unit QlpQRCodeGenLibTypes;
+
+{$I ..\Include\QRCodeGenLib.inc}
+
+interface
+
+uses
+  SysUtils;
+
+type
+
+  EQRCodeGenLibException = class(Exception);
+  EInvalidOperationQRCodeGenLibException = class(EQRCodeGenLibException);
+  EIndexOutOfRangeQRCodeGenLibException = class(EQRCodeGenLibException);
+  EArgumentQRCodeGenLibException = class(EQRCodeGenLibException);
+  EArgumentInvalidQRCodeGenLibException = class(EQRCodeGenLibException);
+  EArgumentNilQRCodeGenLibException = class(EQRCodeGenLibException);
+  EArgumentOutOfRangeQRCodeGenLibException = class(EQRCodeGenLibException);
+  EUnsupportedTypeQRCodeGenLibException = class(EQRCodeGenLibException);
+  ENullReferenceQRCodeGenLibException = class(EQRCodeGenLibException);
+
+  /// <summary>
+  /// Represents a dynamic array of Byte.
+  /// </summary>
+  TQRCodeGenLibByteArray = TBytes;
+
+  /// <summary>
+  /// Represents a dynamic generic array of Type T.
+  /// </summary>
+  TQRCodeGenLibGenericArray<T> = array of T;
+
+{$IFDEF DELPHIXE_UP}
+  /// <summary>
+  /// Represents a dynamic array of Int32.
+  /// </summary>
+  TQRCodeGenLibInt32Array = TArray<Int32>;
+
+  /// <summary>
+  /// Represents a dynamic array of Boolean.
+  /// </summary>
+  TQRCodeGenLibBooleanArray = TArray<Boolean>;
+
+  /// <summary>
+  /// Represents a dynamic array of String.
+  /// </summary>
+  TQRCodeGenLibStringArray = TArray<String>;
+
+  /// <summary>
+  /// Represents a dynamic array of array of Byte.
+  /// </summary>
+  TQRCodeGenLibMatrixByteArray = TArray<TQRCodeGenLibByteArray>;
+
+  /// <summary>
+  /// Represents a dynamic array of array of Int32.
+  /// </summary>
+  TQRCodeGenLibMatrixInt32Array = TArray<TQRCodeGenLibInt32Array>;
+
+{$ELSE}
+  /// <summary>
+  /// Represents a dynamic array of Int32.
+  /// </summary>
+  TQRCodeGenLibInt32Array = array of Int32;
+
+  /// <summary>
+  /// Represents a dynamic array of Boolean.
+  /// </summary>
+  TQRCodeGenLibBooleanArray = array of Boolean;
+
+  /// <summary>
+  /// Represents a dynamic array of String.
+  /// </summary>
+  TQRCodeGenLibStringArray = array of String;
+
+  /// <summary>
+  /// Represents a dynamic array of array of Byte.
+  /// </summary>
+  TQRCodeGenLibMatrixByteArray = array of TQRCodeGenLibByteArray;
+
+  /// <summary>
+  /// Represents a dynamic array of array of Int32.
+  /// </summary>
+  TQRCodeGenLibMatrixInt32Array = array of TQRCodeGenLibInt32Array;
+
+{$ENDIF DELPHIXE_UP}
+
+implementation
+
+{$IFDEF FPC}
+
+initialization
+
+// Set UTF-8 in AnsiStrings, just like Lazarus
+SetMultiByteConversionCodePage(CP_UTF8);
+// SetMultiByteFileSystemCodePage(CP_UTF8); not needed, this is the default under Windows
+SetMultiByteRTLFileSystemCodePage(CP_UTF8);
+{$ENDIF FPC}
+
+end.

+ 51 - 0
README.md

@@ -0,0 +1,51 @@
+# QRCodeGenLib4Pascal [![License](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Xor-el/QRCodeGenLib4Pascal/blob/master/LICENSE)
+QRCodeGenLib4Pascal is a Delphi/FPC Port of [Fast-QR-Code-generator](https://github.com/nayuki/Fast-QR-Code-generator/) written by [Nayuki](https://github.com/nayuki). It provides an easy to use interface for generating QR Codes.
+
+**Build Status**
+[![Build Status](https://travis-ci.org/Xor-el/QRCodeGenLib4Pascal.svg?branch=master)](https://travis-ci.org/Xor-el/QRCodeGenLib4Pascal)
+
+Features
+--------
+
+Core features:
+
+* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard
+* Output formats: Raw modules/pixels of the QR symbol, SVG XML string/file, `ImageObject`(`bmp`, `jpg` (and `png` for Lazarus/FPC)).
+* Encodes numeric and special-alphanumeric text in less space than general text
+* Open source code under the permissive MIT License
+
+Manual parameters:
+
+* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data
+* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one
+* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number
+* User can create a list of data segments manually and add ECI segments
+
+**Supported Compilers**
+ 
+    FreePascal 3.0.0 and Above.
+    
+    Delphi XE3 and Above.
+
+**Installing the Library.**
+
+**Method One:**
+
+ Use the Provided Packages in the "Packages" Folder.
+
+**Method Two:**
+
+ Add the Library Path and Sub Path to your Project Search Path.
+
+**Demos**
+
+ Check out the `QRCodeGenLib.Demo` folder.
+
+**License**
+
+This "Software" is Licensed Under  **`MIT License (MIT)`** .
+
+#### Tip Jar
+* :dollar: **Bitcoin**: `1MhFfW7tDuEHQSgie65uJcAfJgCNchGeKf`
+* :euro: **Ethereum**: `0x6c1DC21aeC49A822A4f1E3bf07c623C2C1978a98`
+* :pound: **Pascalcoin**: `345367-40`