Symbology Identifier support (#1372)

* decoder support for symbology identifier metadata

Co-authored-by: Daniel Dehnhard <daniel@dehnhard.it>
diff --git a/core/src/main/java/com/google/zxing/ResultMetadataType.java b/core/src/main/java/com/google/zxing/ResultMetadataType.java
index 67c5363..4b78ac6 100644
--- a/core/src/main/java/com/google/zxing/ResultMetadataType.java
+++ b/core/src/main/java/com/google/zxing/ResultMetadataType.java
@@ -94,4 +94,8 @@
    */
   STRUCTURED_APPEND_PARITY,
 
+  /**
+   * Barcode Symbology Identifier.
+   */
+  SYMBOLOGY_IDENTIFIER,
 }
diff --git a/core/src/main/java/com/google/zxing/aztec/AztecReader.java b/core/src/main/java/com/google/zxing/aztec/AztecReader.java
index c553e0a..f3a3e21 100644
--- a/core/src/main/java/com/google/zxing/aztec/AztecReader.java
+++ b/core/src/main/java/com/google/zxing/aztec/AztecReader.java
@@ -110,6 +110,7 @@
     if (ecLevel != null) {
       result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
     }
+    result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]z" + decoderResult.getSymbologyModifier());
 
     return result;
   }
diff --git a/core/src/main/java/com/google/zxing/common/DecoderResult.java b/core/src/main/java/com/google/zxing/common/DecoderResult.java
index 9a0d1b1..db2e7e0 100644
--- a/core/src/main/java/com/google/zxing/common/DecoderResult.java
+++ b/core/src/main/java/com/google/zxing/common/DecoderResult.java
@@ -37,12 +37,21 @@
   private Object other;
   private final int structuredAppendParity;
   private final int structuredAppendSequenceNumber;
+  private final int symbologyModifier;
 
   public DecoderResult(byte[] rawBytes,
                        String text,
                        List<byte[]> byteSegments,
                        String ecLevel) {
-    this(rawBytes, text, byteSegments, ecLevel, -1, -1);
+    this(rawBytes, text, byteSegments, ecLevel, -1, -1, 0);
+  }
+
+  public DecoderResult(byte[] rawBytes,
+                       String text,
+                       List<byte[]> byteSegments,
+                       String ecLevel,
+                       int symbologyModifier) {
+    this(rawBytes, text, byteSegments, ecLevel, -1, -1, symbologyModifier);
   }
 
   public DecoderResult(byte[] rawBytes,
@@ -51,6 +60,16 @@
                        String ecLevel,
                        int saSequence,
                        int saParity) {
+    this(rawBytes, text, byteSegments, ecLevel, saSequence, saParity, 0);
+  }
+
+  public DecoderResult(byte[] rawBytes,
+                       String text,
+                       List<byte[]> byteSegments,
+                       String ecLevel,
+                       int saSequence,
+                       int saParity,
+                       int symbologyModifier) {
     this.rawBytes = rawBytes;
     this.numBits = rawBytes == null ? 0 : 8 * rawBytes.length;
     this.text = text;
@@ -58,6 +77,7 @@
     this.ecLevel = ecLevel;
     this.structuredAppendParity = saParity;
     this.structuredAppendSequenceNumber = saSequence;
+    this.symbologyModifier = symbologyModifier;
   }
 
   /**
@@ -149,4 +169,8 @@
     return structuredAppendSequenceNumber;
   }
 
+  public int getSymbologyModifier() {
+    return symbologyModifier;
+  }
+
 }
diff --git a/core/src/main/java/com/google/zxing/datamatrix/DataMatrixReader.java b/core/src/main/java/com/google/zxing/datamatrix/DataMatrixReader.java
index b0f380d..f596fd1 100644
--- a/core/src/main/java/com/google/zxing/datamatrix/DataMatrixReader.java
+++ b/core/src/main/java/com/google/zxing/datamatrix/DataMatrixReader.java
@@ -83,6 +83,7 @@
     if (ecLevel != null) {
       result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
     }
+    result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]d" + decoderResult.getSymbologyModifier());
     return result;
   }
 
diff --git a/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java b/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java
index 9000a78..01a14e7 100644
--- a/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java
+++ b/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java
@@ -23,7 +23,9 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes
@@ -43,7 +45,8 @@
     TEXT_ENCODE,
     ANSIX12_ENCODE,
     EDIFACT_ENCODE,
-    BASE256_ENCODE
+    BASE256_ENCODE,
+    ECI_ENCODE
   }
 
   /**
@@ -87,17 +90,20 @@
     StringBuilder result = new StringBuilder(100);
     StringBuilder resultTrailer = new StringBuilder(0);
     List<byte[]> byteSegments = new ArrayList<>(1);
+    int symbologyModifier = 0;
     Mode mode = Mode.ASCII_ENCODE;
+    Set<Integer> fnc1Positions = new HashSet<Integer>(); // Would be replaceable by looking directly at 'bytes', if we're sure to not having to account for multi byte values.
+    boolean isECIencoded = false;
     do {
       if (mode == Mode.ASCII_ENCODE) {
-        mode = decodeAsciiSegment(bits, result, resultTrailer);
+        mode = decodeAsciiSegment(bits, result, resultTrailer, fnc1Positions);
       } else {
         switch (mode) {
           case C40_ENCODE:
-            decodeC40Segment(bits, result);
+            decodeC40Segment(bits, result, fnc1Positions);
             break;
           case TEXT_ENCODE:
-            decodeTextSegment(bits, result);
+            decodeTextSegment(bits, result, fnc1Positions);
             break;
           case ANSIX12_ENCODE:
             decodeAnsiX12Segment(bits, result);
@@ -108,6 +114,9 @@
           case BASE256_ENCODE:
             decodeBase256Segment(bits, result, byteSegments);
             break;
+          case ECI_ENCODE:
+            isECIencoded = true; // ECI detection only, atm continue decoding as ASCII
+            break;
           default:
             throw FormatException.getFormatInstance();
         }
@@ -117,7 +126,27 @@
     if (resultTrailer.length() > 0) {
       result.append(resultTrailer);
     }
-    return new DecoderResult(bytes, result.toString(), byteSegments.isEmpty() ? null : byteSegments, null);
+    if (isECIencoded) {
+      // Examples for this numbers can be found in this documentation of a hardware barcode scanner:
+      // https://honeywellaidc.force.com/supportppr/s/article/List-of-barcode-symbology-AIM-Identifiers
+      if (fnc1Positions.contains(0) || fnc1Positions.contains(4)) {
+        symbologyModifier = 5;
+      } else if (fnc1Positions.contains(1) || fnc1Positions.contains(5)) {
+        symbologyModifier = 6;
+      } else {
+        symbologyModifier = 4;
+      }
+    } else {
+      if (fnc1Positions.contains(0) || fnc1Positions.contains(4)) {
+        symbologyModifier = 2;
+      } else if (fnc1Positions.contains(1) || fnc1Positions.contains(5)) {
+        symbologyModifier = 3;
+      } else {
+        symbologyModifier = 1;
+      }
+    }
+
+    return new DecoderResult(bytes, result.toString(), byteSegments.isEmpty() ? null : byteSegments, null, symbologyModifier);
   }
 
   /**
@@ -125,7 +154,8 @@
    */
   private static Mode decodeAsciiSegment(BitSource bits,
                                          StringBuilder result,
-                                         StringBuilder resultTrailer) throws FormatException {
+                                         StringBuilder resultTrailer,
+                                         Set<Integer> fnc1positions) throws FormatException {
     boolean upperShift = false;
     do {
       int oneByte = bits.readBits(8);
@@ -153,6 +183,7 @@
           case 231: // Latch to Base 256 encodation
             return Mode.BASE256_ENCODE;
           case 232: // FNC1
+            fnc1positions.add(result.length());
             result.append((char) 29); // translate as ASCII 29
             break;
           case 233: // Structured Append
@@ -178,10 +209,7 @@
           case 240: // Latch to EDIFACT encodation
             return Mode.EDIFACT_ENCODE;
           case 241: // ECI Character
-            // TODO(bbrown): I think we need to support ECI
-            //throw ReaderException.getInstance();
-            // Ignore this symbol for now
-            break;
+            return Mode.ECI_ENCODE;
           default:
             // Not to be used in ASCII encodation
             // but work around encoders that end with 254, latch back to ASCII
@@ -198,7 +226,7 @@
   /**
    * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
    */
-  private static void decodeC40Segment(BitSource bits, StringBuilder result) throws FormatException {
+  private static void decodeC40Segment(BitSource bits, StringBuilder result, Set<Integer> fnc1positions) throws FormatException {
     // Three C40 values are encoded in a 16-bit value as
     // (1600 * C1) + (40 * C2) + C3 + 1
     // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
@@ -258,6 +286,7 @@
             } else {
               switch (cValue) {
                 case 27: // FNC1
+                  fnc1positions.add(result.length());
                   result.append((char) 29); // translate as ASCII 29
                   break;
                 case 30: // Upper Shift
@@ -288,7 +317,7 @@
   /**
    * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
    */
-  private static void decodeTextSegment(BitSource bits, StringBuilder result) throws FormatException {
+  private static void decodeTextSegment(BitSource bits, StringBuilder result, Set<Integer> fnc1positions) throws FormatException {
     // Three Text values are encoded in a 16-bit value as
     // (1600 * C1) + (40 * C2) + C3 + 1
     // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
@@ -348,6 +377,7 @@
             } else {
               switch (cValue) {
                 case 27: // FNC1
+                  fnc1positions.add(result.length());
                   result.append((char) 29); // translate as ASCII 29
                   break;
                 case 30: // Upper Shift
diff --git a/core/src/main/java/com/google/zxing/oned/CodaBarReader.java b/core/src/main/java/com/google/zxing/oned/CodaBarReader.java
index 0ba9f1e..ed8ea76 100644
--- a/core/src/main/java/com/google/zxing/oned/CodaBarReader.java
+++ b/core/src/main/java/com/google/zxing/oned/CodaBarReader.java
@@ -20,6 +20,7 @@
 import com.google.zxing.DecodeHintType;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultMetadataType;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.common.BitArray;
 
@@ -152,13 +153,16 @@
       runningCount += counters[i];
     }
     float right = runningCount;
-    return new Result(
+
+    Result result = new Result(
         decodeRowResult.toString(),
         null,
         new ResultPoint[]{
             new ResultPoint(left, rowNumber),
             new ResultPoint(right, rowNumber)},
         BarcodeFormat.CODABAR);
+    result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]F0");
+    return result;
   }
 
   private void validatePattern(int start) throws NotFoundException {
diff --git a/core/src/main/java/com/google/zxing/oned/Code128Reader.java b/core/src/main/java/com/google/zxing/oned/Code128Reader.java
index a9d2bc2..534da01 100644
--- a/core/src/main/java/com/google/zxing/oned/Code128Reader.java
+++ b/core/src/main/java/com/google/zxing/oned/Code128Reader.java
@@ -22,6 +22,7 @@
 import com.google.zxing.FormatException;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultMetadataType;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.common.BitArray;
 
@@ -238,6 +239,8 @@
 
     boolean convertFNC1 = hints != null && hints.containsKey(DecodeHintType.ASSUME_GS1);
 
+    int symbologyModifier = 0;
+
     int[] startPatternInfo = findStartPattern(row);
     int startCode = startPatternInfo[2];
 
@@ -339,6 +342,11 @@
             }
             switch (code) {
               case CODE_FNC_1:
+                if (result.length() == 0) { // FNC1 at first or second character determines the symbology
+                  symbologyModifier = 1;
+                } else if (result.length() == 1) {
+                  symbologyModifier = 2;
+                }
                 if (convertFNC1) {
                   if (result.length() == 0) {
                     // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
@@ -351,6 +359,8 @@
                 }
                 break;
               case CODE_FNC_2:
+                symbologyModifier = 4;
+                break;
               case CODE_FNC_3:
                 // do nothing?
                 break;
@@ -395,6 +405,11 @@
             }
             switch (code) {
               case CODE_FNC_1:
+                if (result.length() == 0) { // FNC1 at first or second character determines the symbology
+                  symbologyModifier = 1;
+                } else if (result.length() == 1) {
+                  symbologyModifier = 2;
+                }
                 if (convertFNC1) {
                   if (result.length() == 0) {
                     // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
@@ -407,6 +422,8 @@
                 }
                 break;
               case CODE_FNC_2:
+                symbologyModifier = 4;
+                break;
               case CODE_FNC_3:
                 // do nothing?
                 break;
@@ -449,6 +466,11 @@
             }
             switch (code) {
               case CODE_FNC_1:
+                if (result.length() == 0) { // FNC1 at first or second character determines the symbology
+                  symbologyModifier = 1;
+                } else if (result.length() == 1) {
+                  symbologyModifier = 2;
+                }
                 if (convertFNC1) {
                   if (result.length() == 0) {
                     // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
@@ -460,6 +482,9 @@
                   }
                 }
                 break;
+              case CODE_FNC_2:
+                symbologyModifier = 4;
+                break;
               case CODE_CODE_A:
                 codeSet = CODE_CODE_A;
                 break;
@@ -525,14 +550,15 @@
     for (int i = 0; i < rawCodesSize; i++) {
       rawBytes[i] = rawCodes.get(i);
     }
-
-    return new Result(
+    Result resultObject = new Result(
         result.toString(),
         rawBytes,
         new ResultPoint[]{
             new ResultPoint(left, rowNumber),
             new ResultPoint(right, rowNumber)},
         BarcodeFormat.CODE_128);
+    resultObject.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]C" + symbologyModifier);
+    return resultObject;
 
   }
 
diff --git a/core/src/main/java/com/google/zxing/oned/Code39Reader.java b/core/src/main/java/com/google/zxing/oned/Code39Reader.java
index 4b13859..35dfb7a 100644
--- a/core/src/main/java/com/google/zxing/oned/Code39Reader.java
+++ b/core/src/main/java/com/google/zxing/oned/Code39Reader.java
@@ -22,6 +22,7 @@
 import com.google.zxing.FormatException;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultMetadataType;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.common.BitArray;
 
@@ -165,14 +166,16 @@
 
     float left = (start[1] + start[0]) / 2.0f;
     float right = lastStart + lastPatternSize / 2.0f;
-    return new Result(
+
+    Result resultObject = new Result(
         resultString,
         null,
         new ResultPoint[]{
             new ResultPoint(left, rowNumber),
             new ResultPoint(right, rowNumber)},
         BarcodeFormat.CODE_39);
-
+    resultObject.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]A0");
+    return resultObject;
   }
 
   private static int[] findAsteriskPattern(BitArray row, int[] counters) throws NotFoundException {
diff --git a/core/src/main/java/com/google/zxing/oned/Code93Reader.java b/core/src/main/java/com/google/zxing/oned/Code93Reader.java
index c2dddbc..5aa203f 100644
--- a/core/src/main/java/com/google/zxing/oned/Code93Reader.java
+++ b/core/src/main/java/com/google/zxing/oned/Code93Reader.java
@@ -22,6 +22,7 @@
 import com.google.zxing.FormatException;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultMetadataType;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.common.BitArray;
 
@@ -118,14 +119,16 @@
 
     float left = (start[1] + start[0]) / 2.0f;
     float right = lastStart + lastPatternSize / 2.0f;
-    return new Result(
+
+    Result resultObject = new Result(
         resultString,
         null,
         new ResultPoint[]{
             new ResultPoint(left, rowNumber),
             new ResultPoint(right, rowNumber)},
         BarcodeFormat.CODE_93);
-
+    resultObject.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]G0");
+    return resultObject;
   }
 
   private int[] findAsteriskPattern(BitArray row) throws NotFoundException {
diff --git a/core/src/main/java/com/google/zxing/oned/UPCEANReader.java b/core/src/main/java/com/google/zxing/oned/UPCEANReader.java
index 8e271ee..f2a00bb 100644
--- a/core/src/main/java/com/google/zxing/oned/UPCEANReader.java
+++ b/core/src/main/java/com/google/zxing/oned/UPCEANReader.java
@@ -153,6 +153,7 @@
 
     ResultPointCallback resultPointCallback = hints == null ? null :
         (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
+    int symbologyIdentifier = 0;
 
     if (resultPointCallback != null) {
       resultPointCallback.foundPossibleResultPoint(new ResultPoint(
@@ -239,6 +240,11 @@
         decodeResult.putMetadata(ResultMetadataType.POSSIBLE_COUNTRY, countryID);
       }
     }
+    if (format == BarcodeFormat.EAN_8) {
+      symbologyIdentifier = 4;
+    }
+
+    decodeResult.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]E" + symbologyIdentifier);
 
     return decodeResult;
   }
diff --git a/core/src/main/java/com/google/zxing/oned/rss/RSS14Reader.java b/core/src/main/java/com/google/zxing/oned/rss/RSS14Reader.java
index 193c526..704e864 100644
--- a/core/src/main/java/com/google/zxing/oned/rss/RSS14Reader.java
+++ b/core/src/main/java/com/google/zxing/oned/rss/RSS14Reader.java
@@ -20,6 +20,7 @@
 import com.google.zxing.DecodeHintType;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultMetadataType;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.ResultPointCallback;
 import com.google.zxing.common.BitArray;
@@ -131,11 +132,13 @@
 
     ResultPoint[] leftPoints = leftPair.getFinderPattern().getResultPoints();
     ResultPoint[] rightPoints = rightPair.getFinderPattern().getResultPoints();
-    return new Result(
+    Result result = new Result(
         buffer.toString(),
         null,
         new ResultPoint[] { leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1], },
         BarcodeFormat.RSS_14);
+    result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]e0");
+    return result;
   }
 
   private static boolean checkChecksum(Pair leftPair, Pair rightPair) {
diff --git a/core/src/main/java/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java b/core/src/main/java/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java
index bce3526..9b99704 100644
--- a/core/src/main/java/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java
+++ b/core/src/main/java/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java
@@ -31,6 +31,7 @@
 import com.google.zxing.FormatException;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultMetadataType;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.common.BitArray;
 import com.google.zxing.common.detector.MathUtils;
@@ -355,12 +356,14 @@
     ResultPoint[] firstPoints = pairs.get(0).getFinderPattern().getResultPoints();
     ResultPoint[] lastPoints  = pairs.get(pairs.size() - 1).getFinderPattern().getResultPoints();
 
-    return new Result(
+    Result result = new Result(
           resultingString,
           null,
           new ResultPoint[]{firstPoints[0], firstPoints[1], lastPoints[0], lastPoints[1]},
           BarcodeFormat.RSS_EXPANDED
       );
+    result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]e0");
+    return result;
   }
 
   private boolean checkChecksum() {
diff --git a/core/src/main/java/com/google/zxing/pdf417/PDF417Reader.java b/core/src/main/java/com/google/zxing/pdf417/PDF417Reader.java
index 145d606..cf2557c 100644
--- a/core/src/main/java/com/google/zxing/pdf417/PDF417Reader.java
+++ b/core/src/main/java/com/google/zxing/pdf417/PDF417Reader.java
@@ -81,7 +81,7 @@
     }
   }
 
-  private static Result[] decode(BinaryBitmap image, Map<DecodeHintType, ?> hints, boolean multiple) 
+  private static Result[] decode(BinaryBitmap image, Map<DecodeHintType, ?> hints, boolean multiple)
       throws NotFoundException, FormatException, ChecksumException {
     List<Result> results = new ArrayList<>();
     PDF417DetectorResult detectorResult = Detector.detect(image, hints, multiple);
@@ -94,6 +94,7 @@
       if (pdf417ResultMetadata != null) {
         result.putMetadata(ResultMetadataType.PDF417_EXTRA_METADATA, pdf417ResultMetadata);
       }
+      result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]L" + decoderResult.getSymbologyModifier());
       results.add(result);
     }
     return results.toArray(EMPTY_RESULT_ARRAY);
diff --git a/core/src/main/java/com/google/zxing/qrcode/QRCodeReader.java b/core/src/main/java/com/google/zxing/qrcode/QRCodeReader.java
index a4475af..f6a765f 100644
--- a/core/src/main/java/com/google/zxing/qrcode/QRCodeReader.java
+++ b/core/src/main/java/com/google/zxing/qrcode/QRCodeReader.java
@@ -99,6 +99,7 @@
       result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
                          decoderResult.getStructuredAppendParity());
     }
+    result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]Q" + decoderResult.getSymbologyModifier());
     return result;
   }
 
diff --git a/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
index 497ddab..ff11e81 100644
--- a/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
+++ b/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
@@ -58,10 +58,13 @@
     List<byte[]> byteSegments = new ArrayList<>(1);
     int symbolSequence = -1;
     int parityData = -1;
+    int symbologyModifier = 1;
 
     try {
       CharacterSetECI currentCharacterSetECI = null;
       boolean fc1InEffect = false;
+      boolean hasFNC1first = false;
+      boolean hasFNC1second = false;
       Mode mode;
       do {
         // While still another segment to read...
@@ -75,7 +78,12 @@
           case TERMINATOR:
             break;
           case FNC1_FIRST_POSITION:
+            hasFNC1first = true; // symbology detection
+            // We do little with FNC1 except alter the parsed result a bit according to the spec
+            fc1InEffect = true;
+            break;
           case FNC1_SECOND_POSITION:
+            hasFNC1second = true; // symbology detection
             // We do little with FNC1 except alter the parsed result a bit according to the spec
             fc1InEffect = true;
             break;
@@ -128,6 +136,25 @@
             break;
         }
       } while (mode != Mode.TERMINATOR);
+
+      if (currentCharacterSetECI != null) {
+        if (hasFNC1first) {
+          symbologyModifier = 4;
+        } else if (hasFNC1second) {
+          symbologyModifier = 6;
+        } else {
+          symbologyModifier = 2;
+        }
+      } else {
+        if (hasFNC1first) {
+          symbologyModifier = 3;
+        } else if (hasFNC1second) {
+          symbologyModifier = 5;
+        } else {
+          symbologyModifier = 1;
+        }
+      }
+
     } catch (IllegalArgumentException iae) {
       // from readBits() calls
       throw FormatException.getFormatInstance();
@@ -138,7 +165,8 @@
                              byteSegments.isEmpty() ? null : byteSegments,
                              ecLevel == null ? null : ecLevel.toString(),
                              symbolSequence,
-                             parityData);
+                             parityData,
+                             symbologyModifier);
   }
 
   /**
diff --git a/core/src/test/resources/blackbox/aztec-1/abc-19x19C.metadata.txt b/core/src/test/resources/blackbox/aztec-1/abc-19x19C.metadata.txt
new file mode 100644
index 0000000..1f5cf22
--- /dev/null
+++ b/core/src/test/resources/blackbox/aztec-1/abc-19x19C.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]z0
diff --git a/core/src/test/resources/blackbox/codabar-1/01.metadata.txt b/core/src/test/resources/blackbox/codabar-1/01.metadata.txt
new file mode 100644
index 0000000..fb300fc
--- /dev/null
+++ b/core/src/test/resources/blackbox/codabar-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]F0
diff --git a/core/src/test/resources/blackbox/code128-1/01.metadata.txt b/core/src/test/resources/blackbox/code128-1/01.metadata.txt
new file mode 100644
index 0000000..8e0da93
--- /dev/null
+++ b/core/src/test/resources/blackbox/code128-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]C1
diff --git a/core/src/test/resources/blackbox/code128-1/02.metadata.txt b/core/src/test/resources/blackbox/code128-1/02.metadata.txt
new file mode 100644
index 0000000..d93697d
--- /dev/null
+++ b/core/src/test/resources/blackbox/code128-1/02.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]C0
diff --git a/core/src/test/resources/blackbox/code39-1/01.metadata.txt b/core/src/test/resources/blackbox/code39-1/01.metadata.txt
new file mode 100644
index 0000000..cfe8ab1
--- /dev/null
+++ b/core/src/test/resources/blackbox/code39-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]A0
diff --git a/core/src/test/resources/blackbox/code93-1/01.metadata.txt b/core/src/test/resources/blackbox/code93-1/01.metadata.txt
new file mode 100644
index 0000000..6cc5a64
--- /dev/null
+++ b/core/src/test/resources/blackbox/code93-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]G0
diff --git a/core/src/test/resources/blackbox/datamatrix-1/0123456789.metadata.txt b/core/src/test/resources/blackbox/datamatrix-1/0123456789.metadata.txt
new file mode 100644
index 0000000..80c1d0c
--- /dev/null
+++ b/core/src/test/resources/blackbox/datamatrix-1/0123456789.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]d1
diff --git a/core/src/test/resources/blackbox/ean13-1/01.metadata.txt b/core/src/test/resources/blackbox/ean13-1/01.metadata.txt
new file mode 100644
index 0000000..0daadb4
--- /dev/null
+++ b/core/src/test/resources/blackbox/ean13-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]E0
diff --git a/core/src/test/resources/blackbox/ean8-1/01.metadata.txt b/core/src/test/resources/blackbox/ean8-1/01.metadata.txt
new file mode 100644
index 0000000..6f43c99
--- /dev/null
+++ b/core/src/test/resources/blackbox/ean8-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]E4
diff --git a/core/src/test/resources/blackbox/itf-1/01.metadata.txt b/core/src/test/resources/blackbox/itf-1/01.metadata.txt
new file mode 100644
index 0000000..aef8e6d
--- /dev/null
+++ b/core/src/test/resources/blackbox/itf-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]I0
diff --git a/core/src/test/resources/blackbox/pdf417-1/01.metadata.txt b/core/src/test/resources/blackbox/pdf417-1/01.metadata.txt
new file mode 100644
index 0000000..75f38ce
--- /dev/null
+++ b/core/src/test/resources/blackbox/pdf417-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]L0
diff --git a/core/src/test/resources/blackbox/qrcode-1/01.metadata.txt b/core/src/test/resources/blackbox/qrcode-1/01.metadata.txt
new file mode 100644
index 0000000..6f6e83d
--- /dev/null
+++ b/core/src/test/resources/blackbox/qrcode-1/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]Q1
diff --git a/core/src/test/resources/blackbox/qrcode-6/01.metadata.txt b/core/src/test/resources/blackbox/qrcode-6/01.metadata.txt
new file mode 100644
index 0000000..6f6e83d
--- /dev/null
+++ b/core/src/test/resources/blackbox/qrcode-6/01.metadata.txt
@@ -0,0 +1 @@
+SYMBOLOGY_IDENTIFIER=]Q1