  protected void initFile(String id) throws FormatException, IOException {
    debug("BioRadReader.initFile(" + id + ")");
    if (!checkSuffix(id, PIC_SUFFIX)) {
      Location dir = new Location(id).getAbsoluteFile().getParentFile();
      String[] list = dir.list();
      for (int i=0; i<list.length; i++) {
        if (checkSuffix(list[i], PIC_SUFFIX)) {
          id = new Location(dir.getAbsolutePath(), list[i]).getAbsolutePath();
        }
      }
      if (!checkSuffix(id, PIC_SUFFIX)) {
        throw new FormatException("No .pic files found - invalid dataset.");
      }
    }
    super.initFile(id);
    in = new RandomAccessInputStream(id);
    in.order(true);
    used = new Vector<String>();
    used.add(currentId);
    status("Reading image dimensions");
    noteStrings = new Vector();
    core[0].sizeX = in.readShort();
    core[0].sizeY = in.readShort();
    int npic = in.readShort();
    core[0].imageCount = npic;
    int ramp1min = in.readShort();
    int ramp1max = in.readShort();
    boolean notes = in.readInt() != 0;
    core[0].pixelType =
      in.readShort() == 0 ? FormatTools.UINT16 : FormatTools.UINT8;
    int imageNumber = in.readShort();
    String name = in.readString(32);
    int merged = in.readShort();
    int color1 = in.readShort();
    int fileId = in.readShort();
    int ramp2min = in.readShort();
    int ramp2max = in.readShort();
    int color2 = in.readShort();
    int edited = in.readShort();
    int lens = in.readShort();
    float magFactor = in.readFloat();
    if (fileId != PIC_FILE_ID) {
      throw new FormatException("Invalid file header : " + fileId);
    }
    addGlobalMeta("nx", getSizeX());
    addGlobalMeta("ny", getSizeY());
    addGlobalMeta("npic", getImageCount());
    addGlobalMeta("ramp1_min", ramp1min);
    addGlobalMeta("ramp1_max", ramp1max);
    addGlobalMeta("notes", notes);
    addGlobalMeta("image_number", imageNumber);
    addGlobalMeta("name", name);
    addGlobalMeta("merged", MERGE_NAMES[merged]);
    addGlobalMeta("color1", color1);
    addGlobalMeta("file_id", fileId);
    addGlobalMeta("ramp2_min", ramp2min);
    addGlobalMeta("ramp2_max", ramp2max);
    addGlobalMeta("color2", color2);
    addGlobalMeta("edited", edited);
    addGlobalMeta("lens", lens);
    addGlobalMeta("mag_factor", magFactor);
    int imageLen = getSizeX() * getSizeY();
    int bpp = FormatTools.getBytesPerPixel(getPixelType());
    in.skipBytes(bpp * getImageCount() * imageLen + 6);
    core[0].sizeZ = getImageCount();
    core[0].sizeC = 1;
    core[0].sizeT = 1;
    core[0].orderCertain = false;
    core[0].rgb = false;
    core[0].interleaved = false;
    core[0].littleEndian = LITTLE_ENDIAN;
    core[0].metadataComplete = true;
    core[0].falseColor = true;
    status("Reading notes");
    String zoom = null, zstart = null, zstop = null, mag = null;
    String gain1 = null, gain2 = null, gain3 = null;
    String offset1 = null;
    String ex1 = null, ex2 = null, ex3 = null;
    String em1 = null, em2 = null, em3 = null;
    MetadataStore store =
      new FilterMetadata(getMetadataStore(), isMetadataFiltered());
    int nLasers = 0;
    int noteCount = 0;
    boolean brokenNotes = false;
    while (notes) {
      Note n = new Note();
      n.level = in.readShort();
      notes = in.readInt() != 0;
      n.num = in.readShort();
      n.status = in.readShort();
      n.type = in.readShort();
      n.x = in.readShort();
      n.y = in.readShort();
      n.p = in.readString(80);
      if (n.type < 0 || n.type >= NOTE_NAMES.length) {
        notes = false;
        brokenNotes = true;
        break;
      }
      int ndx = n.p.length();
      for (int i=0; i<n.p.length(); i++) {
        if (n.p.charAt(i) == 0) {
          ndx = i;
          break;
        }
      }
      n.p = n.p.substring(0, ndx).trim();
      String value = n.p.replaceAll("=", "");
      Vector v = new Vector();
      StringTokenizer t = new StringTokenizer(value, " ");
      while (t.hasMoreTokens()) {
        String token = t.nextToken().trim();
        if (token.length() > 0) v.add(token);
      }
      String[] tokens = (String[]) v.toArray(new String[0]);
      try {
        if (tokens.length > 1) {
          int noteType = Integer.parseInt(tokens[1]);
          if (noteType == 2 && value.indexOf("AXIS_4") != -1) {
            core[0].sizeZ = 1;
            core[0].sizeT = getImageCount();
            core[0].orderCertain = true;
          }
        }
      }
      catch (NumberFormatException e) { }
      noteCount++;
      noteStrings.add(n);
    }
    status("Reading color table");
    int numLuts = 0;
    lut = new byte[3][3][256];
    boolean eof = false;
    int next = 0;
    while (!eof && numLuts < 3 && !brokenNotes) {
      if (in.getFilePointer() + lut[numLuts][next].length <= in.length()) {
        in.read(lut[numLuts][next++]);
        if (next == 3) {
          next = 0;
          numLuts++;
        }
      }
      else eof = true;
      if (eof && numLuts == 0) lut = null;
    }
    if (brokenNotes) lut = null;
    String message = numLuts + " color table" + (numLuts == 1 ? "" : "s") +
      " present.";
    debug(message, 2);
    status("Populating metadata");
    Location parent = new Location(currentId).getAbsoluteFile().getParentFile();
    String[] list = parent.list();
    Arrays.sort(list);
    Vector pics = new Vector();
    for (int i=0; i<list.length; i++) {
      if (list[i].endsWith("lse.xml")) {
        String path =
          new Location(parent.getAbsolutePath(), list[i]).getAbsolutePath();
        RandomAccessInputStream raw = new RandomAccessInputStream(path);
        used.add(path);
        byte[] xml = new byte[(int) raw.length()];
        raw.read(xml);
        raw.close();
        DefaultHandler handler = new BioRadHandler();
        DataTools.parseXML(xml, handler);
        for (int q=0; q<list.length; q++) {
          if (checkSuffix(list[q], PIC_SUFFIX)) {
            path =
              new Location(parent.getAbsolutePath(), list[q]).getAbsolutePath();
            pics.add(path);
            if (!used.contains(path)) used.add(path);
          }
        }
      }
      else if (list[i].endsWith("data.raw")) {
        used.add(
          new Location(parent.getAbsolutePath(), list[i]).getAbsolutePath());
      }
    }
    picFiles = (String[]) pics.toArray(new String[0]);
    core[0].indexed = lut != null;
    core[0].dimensionOrder = "XYCTZ";
    if (picFiles.length > 0) {
      core[0].imageCount = npic * picFiles.length;
      core[0].sizeC = getImageCount() / (getSizeZ() * getSizeT());
    }
    else picFiles = null;
    for (int q=0; q<noteStrings.size(); q++) {
      Note n = (Note) noteStrings.get(q);
      switch (n.type) {
        case NOTE_TYPE_USER:
          addGlobalMeta("Note #" + noteCount, n.toString());
          break;
        case NOTE_TYPE_SCALEBAR:
          addGlobalMeta("Note #" + noteCount, n.toString());
          break;
        case NOTE_TYPE_ARROW:
          addGlobalMeta("Note #" + noteCount, n.toString());
          break;
        case NOTE_TYPE_VARIABLE:
          if (n.p.indexOf("=") >= 0) {
            String key = n.p.substring(0, n.p.indexOf("=")).trim();
            String value = n.p.substring(n.p.indexOf("=") + 1).trim();
            addGlobalMeta(key, value);
            if (key.equals("INFO_OBJECTIVE_NAME")) {
              store.setObjectiveModel(value, 0, 0);
            }
            else if (key.equals("INFO_OBJECTIVE_MAGNIFICATION")) {
              store.setObjectiveNominalMagnification(
                new Integer((int) Double.parseDouble(value)), 0, 0);
            }
            else if (key.equals("LENS_MAGNIFICATION")) {
              store.setObjectiveNominalMagnification(
                new Integer((int) Double.parseDouble(value)), 0, 0);
            }
            else if (key.startsWith("SETTING")) {
              if (key.indexOf("_DET_") != -1) {
                int index = key.indexOf("_DET_") + 5;
                if (key.lastIndexOf("_") > index) {
                  String idx = key.substring(index, key.indexOf("_", index));
                  int detector = Integer.parseInt(idx) - 1;
                  if (key.endsWith("OFFSET")) {
                    store.setDetectorSettingsOffset(
                      new Float(value), 0, detector);
                  }
                  else if (key.endsWith("GAIN")) {
                    store.setDetectorSettingsGain(
                      new Float(value), 0, detector);
                  }
                  store.setDetectorType("Unknown", 0, detector);
                  store.setDetectorID("Detector:" + detector, 0, detector);
                  store.setDetectorSettingsDetector("Detector:" + detector, 0,
                    detector);
                }
              }
            }
            else {
              String[] values = value.split(" ");
              if (values.length > 1) {
                try {
                  int type = Integer.parseInt(values[0]);
                  if (type == 257 && values.length >= 3) {
                    float start = Float.parseFloat(values[1]);
                    float end = Float.parseFloat(values[2]);
                    float axisLength = end - start;
                    if (key.equals("AXIS_2")) {
                      float pixelSize = axisLength / getSizeX();
                      store.setDimensionsPhysicalSizeX(
                        new Float(pixelSize), 0, 0);
                    }
                    else if (key.equals("AXIS_3")) {
                      float pixelSize = axisLength / getSizeY();
                      store.setDimensionsPhysicalSizeY(
                        new Float(pixelSize), 0, 0);
                    }
                  }
                }
                catch (NumberFormatException e) { }
              }
            }
          }
          else {
            addGlobalMeta("Note #" + noteCount, n.toString());
          }
          break;
        case NOTE_TYPE_STRUCTURE:
          int structureType = (n.x & 0xff00) >> 8;
          int version = (n.x & 0xff);
          String[] values = n.p.split(" ");
          if (structureType == 1) {
            switch (n.y) {
              case 1:
                addGlobalMeta("Scan Channel", values[0]);
                addGlobalMeta("Both mode", values[1]);
                addGlobalMeta("Speed", values[2]);
                addGlobalMeta("Filter", values[3]);
                addGlobalMeta("Factor", values[4]);
                addGlobalMeta("Number of scans", values[5]);
                addGlobalMeta("Photon counting mode (channel 1)", values[6]);
                addGlobalMeta("Photon counting detector (channel 1)",
                  values[7]);
                addGlobalMeta("Photon counting mode (channel 2)", values[8]);
                addGlobalMeta("Photon counting detector (channel 2)",
                  values[9]);
                addGlobalMeta("Photon mode", values[10]);
                addGlobalMeta("Objective magnification", values[11]);
                addGlobalMeta("Zoom factor", values[12]);
                addGlobalMeta("Motor on", values[13]);
                addGlobalMeta("Z Step Size", values[14]);
                store.setObjectiveNominalMagnification(
                  new Integer((int) Double.parseDouble(values[11])), 0, 0);
                store.setDisplayOptionsZoom(new Float(values[12]), 0);
                store.setDimensionsPhysicalSizeZ(new Float(values[14]), 0, 0);
                break;
              case 2:
                addGlobalMeta("Z Start", values[0]);
                addGlobalMeta("Z Stop", values[1]);
                addGlobalMeta("Scan area X coordinate", values[2]);
                addGlobalMeta("Scan area Y coordinate", values[3]);
                addGlobalMeta("Scan area width", values[4]);
                addGlobalMeta("Scan area height", values[5]);
                float width =
                  Float.parseFloat(values[4]) - Float.parseFloat(values[2]);
                width /= getSizeX();
                float height =
                  Float.parseFloat(values[5]) - Float.parseFloat(values[3]);
                height /= getSizeY();
                store.setDimensionsPhysicalSizeX(new Float(width), 0, 0);
                store.setDimensionsPhysicalSizeY(new Float(height), 0, 0);
                break;
              case 3:
                for (int i=0; i<3; i++) {
                  addGlobalMeta("Iris for PMT " + (i + 1), values[i * 4]);
                  addGlobalMeta("Gain for PMT " + (i + 1), values[i * 4 + 1]);
                  addGlobalMeta("Black level for PMT " + (i + 1),
                    values[i * 4 + 2]);
                  addGlobalMeta("Emission filter for PMT " + (i+1),
                    values[i*4] + 3);
                  addGlobalMeta("Multiplier for channel " + (i+1),
                    values[12 + i]);
                }
                break;
              case 4:
                nLasers = Integer.parseInt(values[0]);
                addGlobalMeta("Number of lasers", values[0]);
                addGlobalMeta("Number of transmission detectors", values[1]);
                addGlobalMeta("Number of PMTs", values[2]);
                for (int i=1; i<=3; i++) {
                  int idx = (i + 1) * 3;
                  addGlobalMeta("Shutter present for laser " + i,
                    values[i + 2]);
                  addGlobalMeta("Neutral density filter for laser " + i,
                    values[idx]);
                  addGlobalMeta("Excitation filter for laser " + i,
                    values[idx + 1]);
                  addGlobalMeta("Use laser " + i, values[idx + 2]);
                }
                for (int i=0; i<nLasers; i++) {
                  addGlobalMeta("Neutral density filter name - laser " +
                    (i + 1), values[15 + i]);
                }
                break;
              case 5:
                for (int i=0; i<nLasers; i++) {
                  addGlobalMeta("Excitation filter name - laser " + (i+1),
                    values[i]);
                }
                break;
              case 6:
                for (int i=0; i<nLasers; i++) {
                  addGlobalMeta("Emission filter name - laser " + (i+1),
                    values[i]);
                }
                break;
              case 7:
                for (int i=0; i<2; i++) {
                  String prefix = "Mixer " + i;
                  addGlobalMeta(prefix + " - enhanced", values[i*7]);
                  addGlobalMeta(prefix + " - PMT 1 percentage",
                    values[i*7 + 1]);
                  addGlobalMeta(prefix + " - PMT 2 percentage",
                    values[i*7 + 2]);
                  addGlobalMeta(prefix + " - PMT 3 percentage",
                    values[i*7 + 3]);
                  addGlobalMeta(prefix + " - Transmission 1 percentage",
                    values[i * 7 + 4]);
                  addGlobalMeta(prefix + " - Transmission 2 percentage",
                    values[i * 7 + 5]);
                  addGlobalMeta(prefix + " - Transmission 3 percentage",
                    values[i * 7 + 6]);
                }
                addGlobalMeta("Mixer 0 - low signal on", values[14]);
                addGlobalMeta("Mixer 1 - low signal on", values[15]);
                break;
              case 8:
                addGlobalMeta("Laser name - laser 1", values[0]);
                break;
              case 9:
                addGlobalMeta("Laser name - laser 2", values[0]);
                break;
              case 10:
                addGlobalMeta("Laser name - laser 3", values[0]);
                break;
              case 11:
                for (int i=0; i<3; i++) {
                  String prefix = "Transmission detector " + (i + 1) + " - ";
                  addGlobalMeta(prefix + "offset", values[i * 3]);
                  addGlobalMeta(prefix + "gain", values[i * 3 + 1]);
                  addGlobalMeta(prefix + "black level", values[i * 3 + 2]);
                  store.setDetectorOffset(new Float(values[i * 3]), 0, i);
                  store.setDetectorGain(new Float(values[i * 3 + 1]), 0, i);
                  store.setDetectorType("Unknown", 0, i);
                }
                break;
              case 12:
                for (int i=0; i<2; i++) {
                  prefix = "Part number for ";
                  addGlobalMeta(prefix + "laser " + (i + 1), values[i * 4]);
                  addGlobalMeta(prefix + "excitation filter for laser " +
                    (i + 1), values[i * 4 + 1]);
                  addGlobalMeta(prefix + "ND filter for laser " + (i + 1),
                    values[i * 4 + 2]);
                  addGlobalMeta(prefix + "emission filter for laser " + (i + 1),
                    values[i * 4 + 3]);
                }
                break;
              case 13:
                  addGlobalMeta("Part number for laser 3", values[0]);
                  addGlobalMeta("Part number for excitation filter for laser 3",
                    values[1]);
                  addGlobalMeta("Part number for ND filter for laser 3",
                    values[2]);
                  addGlobalMeta("Part number for emission filter for laser 3",
                    values[3]);
                  addGlobalMeta("Part number for filter block 1", values[4]);
                  addGlobalMeta("Part number for filter block 2", values[5]);
                break;
              case 14:
                addGlobalMeta("Filter Block Name - filter block 1", values[0]);
                addGlobalMeta("Filter Block Name - filter block 2", values[1]);
                break;
              case 15:
                for (int i=0; i<5; i++) {
                  addGlobalMeta("Image bands status - band " + (i + 1),
                    values[i*3]);
                  addGlobalMeta("Image bands min - band " + (i + 1),
                    values[i*3 + 1]);
                  addGlobalMeta("Image bands max - band " + (i + 1),
                    values[i*3 + 2]);
                  if (store instanceof IMinMaxStore) {
                    ((IMinMaxStore) store).setChannelGlobalMinMax(i,
                      Double.parseDouble(values[i*3 + 1]),
                      Double.parseDouble(values[i*3 + 2]), 0);
                  }
                }
                break;
              case 17:
                int year = Integer.parseInt(values[5]) + 1900;
                for (int i=0; i<5; i++) {
                  if (values[i].length() == 1) values[i] = "0" + values[i];
                }
                String date = year + "-" + values[4] + "-" + values[3] + "T" +
                  values[2] + ":" + values[1] + ":" + values[0];
                addGlobalMeta("Acquisition date", date);
                store.setImageCreationDate(date, 0);
                break;
              case 18:
                addGlobalMeta("Mixer 3 - enhanced", values[0]);
                for (int i=1; i<=3; i++) {
                  addGlobalMeta("Mixer 3 - PMT " + i + " percentage",
                    values[i]);
                  addGlobalMeta("Mixer 3 - Transmission " + i + " percentage",
                    values[i + 3]);
                  addGlobalMeta("Mixer 3 - photon counting " + i,
                    values[i + 7]);
                }
                addGlobalMeta("Mixer 3 - low signal on", values[7]);
                addGlobalMeta("Mixer 3 - mode", values[11]);
                break;
              case 19:
                for (int i=1; i<=2; i++) {
                  String prefix = "Mixer " + i;
                  addGlobalMeta(prefix + " - photon counting 1",
                    values[i * 4 - 4]);
                  addGlobalMeta(prefix + " - photon counting 2",
                    values[i * 4 - 3]);
                  addGlobalMeta(prefix + " - photon counting 3",
                    values[i * 4 - 2]);
                  addGlobalMeta(prefix + " - mode", values[i * 4 - 1]);
                }
                break;
              case 20:
                addGlobalMeta("Display mode", values[0]);
                addGlobalMeta("Course", values[1]);
                addGlobalMeta("Time Course - experiment type", values[2]);
                addGlobalMeta("Time Course - kd factor", values[3]);
                store.setExperimentType(values[2], 0);
                break;
              case 21:
                addGlobalMeta("Time Course - ion name", values[0]);
                break;
              case 22:
                addGlobalMeta("PIC file generated on Isoscan (lite)",
                  values[0]);
                for (int i=1; i<=3; i++) {
                  addGlobalMeta("Photon counting used (PMT " + i + ")",
                    values[i]);
                  addGlobalMeta("Hot spot filter used (PMT " + i + ")",
                    values[i+3]);
                  addGlobalMeta("Tx Selector used (TX " + i + ")",
                    values[i + 6]);
                }
                break;
            }
          }
          break;
        default:
          addGlobalMeta("Note #" + noteCount, n.toString());
      }
      if (n.p.indexOf("AXIS") != -1) {
        n.p = n.p.replaceAll("=", "");
        Vector v = new Vector();
        StringTokenizer tokens = new StringTokenizer(n.p, " ");
        while (tokens.hasMoreTokens()) {
          String token = tokens.nextToken().trim();
          if (token.length() > 0) v.add(token);
        }
        String[] values = (String[]) v.toArray(new String[0]);
        String key = values[0];
        String noteType = values[1];
        int axisType = Integer.parseInt(noteType);
        if (values.length > 2) {
          switch (axisType) {
            case 1:
              String dx = values[2];
              String dy = values[3];
              addGlobalMeta(key + " distance (X) in microns", dx);
              addGlobalMeta(key + " distance (Y) in microns", dy);
              break;
            case 3:
              addGlobalMeta(key + " angle (X) in degrees", values[2]);
              addGlobalMeta(key + " angle (Y) in degrees", values[3]);
              break;
            case 4:
              addGlobalMeta(key + " intensity (X)", values[2]);
              addGlobalMeta(key + " intensity (Y)", values[3]);
              break;
            case 6:
              addGlobalMeta(key + " ratio (X)", values[2]);
              addGlobalMeta(key + " ratio (Y)", values[3]);
              break;
            case 7:
              addGlobalMeta(key + " log ratio (X)", values[2]);
              addGlobalMeta(key + " log ratio (Y)", values[3]);
              break;
            case 9:
              addGlobalMeta(key + " noncalibrated intensity min", values[2]);
              addGlobalMeta(key + " noncalibrated intensity max", values[3]);
              addGlobalMeta(key + " calibrated intensity min", values[4]);
              addGlobalMeta(key + " calibrated intensity max", values[5]);
              break;
            case 11:
              addGlobalMeta(key + " RGB type (X)", values[2]);
              addGlobalMeta(key + " RGB type (Y)", values[3]);
              if (key.equals("AXIS_4")) {
                core[0].sizeC = getImageCount();
                core[0].sizeZ = 1;
                core[0].sizeT = 1;
              }
              break;
            case 14:
              addGlobalMeta(key + " time course type (X)", values[2]);
              addGlobalMeta(key + " time course type (Y)", values[3]);
              break;
            case 15:
              String prefix = " inverse sigmoid calibrated intensity ";
              addGlobalMeta(key + prefix + "(min)", values[2]);
              addGlobalMeta(key + prefix + "(max)", values[3]);
              addGlobalMeta(key + prefix + "(beta)", values[4]);
              addGlobalMeta(key + prefix + "(Kd)", values[5]);
              break;
            case 16:
              prefix = " log inverse sigmoid calibrated intensity ";
              addGlobalMeta(key + prefix + "(min)", values[2]);
              addGlobalMeta(key + prefix + "(max)", values[3]);
              addGlobalMeta(key + prefix + "(beta)", values[4]);
              addGlobalMeta(key + prefix + "(Kd)", values[5]);
              break;
          }
        }
      }
    }
    MetadataTools.populatePixels(store, this);
    MetadataTools.setDefaultCreationDate(store, id, 0);
    store.setImageName(name, 0);
    store.setInstrumentID("Instrument:0", 0);
    store.setImageInstrumentRef("Instrument:0", 0);
    store.setObjectiveID("Objective:0", 0, 0);
    store.setObjectiveSettingsObjective("Objective:0", 0);
    store.setObjectiveLensNA(new Float(lens), 0, 0);
    store.setObjectiveNominalMagnification(new Integer((int) magFactor), 0, 0);
    store.setObjectiveCorrection("Unknown", 0, 0);
    store.setObjectiveImmersion("Unknown", 0, 0);
  }

  protected void initFile(String id) throws FormatException, IOException {
    debug("BioRadReader.initFile(" + id + ")");
    if (!checkSuffix(id, PIC_SUFFIX)) {
      Location dir = new Location(id).getAbsoluteFile().getParentFile();
      String[] list = dir.list();
      for (int i=0; i<list.length; i++) {
        if (checkSuffix(list[i], PIC_SUFFIX)) {
          id = new Location(dir.getAbsolutePath(), list[i]).getAbsolutePath();
        }
      }
      if (!checkSuffix(id, PIC_SUFFIX)) {
        throw new FormatException("No .pic files found - invalid dataset.");
      }
    }
    super.initFile(id);
    in = new RandomAccessInputStream(id);
    in.order(true);
    used = new Vector<String>();
    used.add(currentId);
    status("Reading image dimensions");
    noteStrings = new Vector();
    core[0].sizeX = in.readShort();
    core[0].sizeY = in.readShort();
    int npic = in.readShort();
    core[0].imageCount = npic;
    int ramp1min = in.readShort();
    int ramp1max = in.readShort();
    boolean notes = in.readInt() != 0;
    core[0].pixelType =
      in.readShort() == 0 ? FormatTools.UINT16 : FormatTools.UINT8;
    int imageNumber = in.readShort();
    String name = in.readString(32);
    int merged = in.readShort();
    int color1 = in.readShort();
    int fileId = in.readShort();
    int ramp2min = in.readShort();
    int ramp2max = in.readShort();
    int color2 = in.readShort();
    int edited = in.readShort();
    int lens = in.readShort();
    float magFactor = in.readFloat();
    if (fileId != PIC_FILE_ID) {
      throw new FormatException("Invalid file header : " + fileId);
    }
    addGlobalMeta("nx", getSizeX());
    addGlobalMeta("ny", getSizeY());
    addGlobalMeta("npic", getImageCount());
    addGlobalMeta("ramp1_min", ramp1min);
    addGlobalMeta("ramp1_max", ramp1max);
    addGlobalMeta("notes", notes);
    addGlobalMeta("image_number", imageNumber);
    addGlobalMeta("name", name);
    addGlobalMeta("merged", MERGE_NAMES[merged]);
    addGlobalMeta("color1", color1);
    addGlobalMeta("file_id", fileId);
    addGlobalMeta("ramp2_min", ramp2min);
    addGlobalMeta("ramp2_max", ramp2max);
    addGlobalMeta("color2", color2);
    addGlobalMeta("edited", edited);
    addGlobalMeta("lens", lens);
    addGlobalMeta("mag_factor", magFactor);
    int imageLen = getSizeX() * getSizeY();
    int bpp = FormatTools.getBytesPerPixel(getPixelType());
    in.skipBytes(bpp * getImageCount() * imageLen + 6);
    core[0].sizeZ = getImageCount();
    core[0].sizeC = 1;
    core[0].sizeT = 1;
    core[0].orderCertain = false;
    core[0].rgb = false;
    core[0].interleaved = false;
    core[0].littleEndian = LITTLE_ENDIAN;
    core[0].metadataComplete = true;
    core[0].falseColor = true;
    status("Reading notes");
    String zoom = null, zstart = null, zstop = null, mag = null;
    String gain1 = null, gain2 = null, gain3 = null;
    String offset1 = null;
    String ex1 = null, ex2 = null, ex3 = null;
    String em1 = null, em2 = null, em3 = null;
    MetadataStore store =
      new FilterMetadata(getMetadataStore(), isMetadataFiltered());
    int nLasers = 0;
    int noteCount = 0;
    boolean brokenNotes = false;
    while (notes) {
      Note n = new Note();
      n.level = in.readShort();
      notes = in.readInt() != 0;
      n.num = in.readShort();
      n.status = in.readShort();
      n.type = in.readShort();
      n.x = in.readShort();
      n.y = in.readShort();
      n.p = in.readString(80);
      if (n.type < 0 || n.type >= NOTE_NAMES.length) {
        notes = false;
        brokenNotes = true;
        break;
      }
      int ndx = n.p.length();
      for (int i=0; i<n.p.length(); i++) {
        if (n.p.charAt(i) == 0) {
          ndx = i;
          break;
        }
      }
      n.p = n.p.substring(0, ndx).trim();
      String value = n.p.replaceAll("=", "");
      Vector v = new Vector();
      StringTokenizer t = new StringTokenizer(value, " ");
      while (t.hasMoreTokens()) {
        String token = t.nextToken().trim();
        if (token.length() > 0) v.add(token);
      }
      String[] tokens = (String[]) v.toArray(new String[0]);
      try {
        if (tokens.length > 1) {
          int noteType = Integer.parseInt(tokens[1]);
          if (noteType == 2 && value.indexOf("AXIS_4") != -1) {
            core[0].sizeZ = 1;
            core[0].sizeT = getImageCount();
            core[0].orderCertain = true;
          }
        }
      }
      catch (NumberFormatException e) { }
      noteCount++;
      noteStrings.add(n);
    }
    status("Reading color table");
    int numLuts = 0;
    lut = new byte[3][3][256];
    boolean eof = false;
    int next = 0;
    while (!eof && numLuts < 3 && !brokenNotes) {
      if (in.getFilePointer() + lut[numLuts][next].length <= in.length()) {
        in.read(lut[numLuts][next++]);
        if (next == 3) {
          next = 0;
          numLuts++;
        }
      }
      else eof = true;
      if (eof && numLuts == 0) lut = null;
    }
    if (brokenNotes) lut = null;
    String message = numLuts + " color table" + (numLuts == 1 ? "" : "s") +
      " present.";
    debug(message, 2);
    status("Populating metadata");
    Location parent = new Location(currentId).getAbsoluteFile().getParentFile();
    String[] list = parent.list();
    Arrays.sort(list);
    Vector pics = new Vector();
    for (int i=0; i<list.length; i++) {
      if (list[i].endsWith("lse.xml")) {
        String path =
          new Location(parent.getAbsolutePath(), list[i]).getAbsolutePath();
        RandomAccessInputStream raw = new RandomAccessInputStream(path);
        used.add(path);
        byte[] xml = new byte[(int) raw.length()];
        raw.read(xml);
        raw.close();
        DefaultHandler handler = new BioRadHandler();
        DataTools.parseXML(xml, handler);
        for (int q=0; q<list.length; q++) {
          if (checkSuffix(list[q], PIC_SUFFIX)) {
            path =
              new Location(parent.getAbsolutePath(), list[q]).getAbsolutePath();
            pics.add(path);
            if (!used.contains(path)) used.add(path);
          }
        }
      }
      else if (list[i].endsWith("data.raw")) {
        used.add(
          new Location(parent.getAbsolutePath(), list[i]).getAbsolutePath());
      }
    }
    picFiles = (String[]) pics.toArray(new String[0]);
    core[0].indexed = lut != null;
    core[0].dimensionOrder = "XYCTZ";
    if (picFiles.length > 0) {
      core[0].imageCount = npic * picFiles.length;
      core[0].sizeC = getImageCount() / (getSizeZ() * getSizeT());
    }
    else picFiles = null;
    for (int q=0; q<noteStrings.size(); q++) {
      Note n = (Note) noteStrings.get(q);
      switch (n.type) {
        case NOTE_TYPE_USER:
          addGlobalMeta("Note #" + noteCount, n.toString());
          break;
        case NOTE_TYPE_SCALEBAR:
          addGlobalMeta("Note #" + noteCount, n.toString());
          break;
        case NOTE_TYPE_ARROW:
          addGlobalMeta("Note #" + noteCount, n.toString());
          break;
        case NOTE_TYPE_VARIABLE:
          if (n.p.indexOf("=") >= 0) {
            String key = n.p.substring(0, n.p.indexOf("=")).trim();
            String value = n.p.substring(n.p.indexOf("=") + 1).trim();
            addGlobalMeta(key, value);
            if (key.equals("INFO_OBJECTIVE_NAME")) {
              store.setObjectiveModel(value, 0, 0);
            }
            else if (key.equals("INFO_OBJECTIVE_MAGNIFICATION")) {
              store.setObjectiveNominalMagnification(
                new Integer((int) Double.parseDouble(value)), 0, 0);
            }
            else if (key.equals("LENS_MAGNIFICATION")) {
              store.setObjectiveNominalMagnification(
                new Integer((int) Double.parseDouble(value)), 0, 0);
            }
            else if (key.startsWith("SETTING")) {
              if (key.indexOf("_DET_") != -1) {
                int index = key.indexOf("_DET_") + 5;
                if (key.lastIndexOf("_") > index) {
                  String idx = key.substring(index, key.indexOf("_", index));
                  int detector = Integer.parseInt(idx) - 1;
                  if (key.endsWith("OFFSET")) {
                    store.setDetectorSettingsOffset(
                      new Float(value), 0, detector);
                  }
                  else if (key.endsWith("GAIN")) {
                    store.setDetectorSettingsGain(
                      new Float(value), 0, detector);
                  }
                  store.setDetectorType("Unknown", 0, detector);
                  store.setDetectorID("Detector:" + detector, 0, detector);
                  store.setDetectorSettingsDetector("Detector:" + detector, 0,
                    detector);
                }
              }
            }
            else {
              String[] values = value.split(" ");
              if (values.length > 1) {
                try {
                  int type = Integer.parseInt(values[0]);
                  if (type == 257 && values.length >= 3) {
                    float start = Float.parseFloat(values[1]);
                    float end = Float.parseFloat(values[2]);
                    float axisLength = end - start;
                    if (key.equals("AXIS_2")) {
                      float pixelSize = axisLength / getSizeX();
                      store.setDimensionsPhysicalSizeX(
                        new Float(pixelSize), 0, 0);
                    }
                    else if (key.equals("AXIS_3")) {
                      float pixelSize = axisLength / getSizeY();
                      store.setDimensionsPhysicalSizeY(
                        new Float(pixelSize), 0, 0);
                    }
                  }
                }
                catch (NumberFormatException e) { }
              }
            }
          }
          else {
            addGlobalMeta("Note #" + noteCount, n.toString());
          }
          break;
        case NOTE_TYPE_STRUCTURE:
          int structureType = (n.x & 0xff00) >> 8;
          int version = (n.x & 0xff);
          String[] values = n.p.split(" ");
          if (structureType == 1) {
            switch (n.y) {
              case 1:
                addGlobalMeta("Scan Channel", values[0]);
                addGlobalMeta("Both mode", values[1]);
                addGlobalMeta("Speed", values[2]);
                addGlobalMeta("Filter", values[3]);
                addGlobalMeta("Factor", values[4]);
                addGlobalMeta("Number of scans", values[5]);
                addGlobalMeta("Photon counting mode (channel 1)", values[6]);
                addGlobalMeta("Photon counting detector (channel 1)",
                  values[7]);
                addGlobalMeta("Photon counting mode (channel 2)", values[8]);
                addGlobalMeta("Photon counting detector (channel 2)",
                  values[9]);
                addGlobalMeta("Photon mode", values[10]);
                addGlobalMeta("Objective magnification", values[11]);
                addGlobalMeta("Zoom factor", values[12]);
                addGlobalMeta("Motor on", values[13]);
                addGlobalMeta("Z Step Size", values[14]);
                store.setObjectiveNominalMagnification(
                  new Integer((int) Double.parseDouble(values[11])), 0, 0);
                store.setDisplayOptionsZoom(new Float(values[12]), 0);
                store.setDimensionsPhysicalSizeZ(new Float(values[14]), 0, 0);
                break;
              case 2:
                addGlobalMeta("Z Start", values[0]);
                addGlobalMeta("Z Stop", values[1]);
                addGlobalMeta("Scan area X coordinate", values[2]);
                addGlobalMeta("Scan area Y coordinate", values[3]);
                addGlobalMeta("Scan area width", values[4]);
                addGlobalMeta("Scan area height", values[5]);
                float width =
                  Float.parseFloat(values[4]) - Float.parseFloat(values[2]);
                width /= getSizeX();
                float height =
                  Float.parseFloat(values[5]) - Float.parseFloat(values[3]);
                height /= getSizeY();
                store.setDimensionsPhysicalSizeX(new Float(width), 0, 0);
                store.setDimensionsPhysicalSizeY(new Float(height), 0, 0);
                break;
              case 3:
                for (int i=0; i<3; i++) {
                  addGlobalMeta("Iris for PMT " + (i + 1), values[i * 4]);
                  addGlobalMeta("Gain for PMT " + (i + 1), values[i * 4 + 1]);
                  addGlobalMeta("Black level for PMT " + (i + 1),
                    values[i * 4 + 2]);
                  addGlobalMeta("Emission filter for PMT " + (i+1),
                    values[i*4] + 3);
                  addGlobalMeta("Multiplier for channel " + (i+1),
                    values[12 + i]);
                }
                break;
              case 4:
                nLasers = Integer.parseInt(values[0]);
                addGlobalMeta("Number of lasers", values[0]);
                addGlobalMeta("Number of transmission detectors", values[1]);
                addGlobalMeta("Number of PMTs", values[2]);
                for (int i=1; i<=3; i++) {
                  int idx = (i + 1) * 3;
                  addGlobalMeta("Shutter present for laser " + i,
                    values[i + 2]);
                  addGlobalMeta("Neutral density filter for laser " + i,
                    values[idx]);
                  addGlobalMeta("Excitation filter for laser " + i,
                    values[idx + 1]);
                  addGlobalMeta("Use laser " + i, values[idx + 2]);
                }
                for (int i=0; i<nLasers; i++) {
                  addGlobalMeta("Neutral density filter name - laser " +
                    (i + 1), values[15 + i]);
                }
                break;
              case 5:
                for (int i=0; i<nLasers; i++) {
                  addGlobalMeta("Excitation filter name - laser " + (i+1),
                    values[i]);
                }
                break;
              case 6:
                for (int i=0; i<nLasers; i++) {
                  addGlobalMeta("Emission filter name - laser " + (i+1),
                    values[i]);
                }
                break;
              case 7:
                for (int i=0; i<2; i++) {
                  String prefix = "Mixer " + i;
                  addGlobalMeta(prefix + " - enhanced", values[i*7]);
                  addGlobalMeta(prefix + " - PMT 1 percentage",
                    values[i*7 + 1]);
                  addGlobalMeta(prefix + " - PMT 2 percentage",
                    values[i*7 + 2]);
                  addGlobalMeta(prefix + " - PMT 3 percentage",
                    values[i*7 + 3]);
                  addGlobalMeta(prefix + " - Transmission 1 percentage",
                    values[i * 7 + 4]);
                  addGlobalMeta(prefix + " - Transmission 2 percentage",
                    values[i * 7 + 5]);
                  addGlobalMeta(prefix + " - Transmission 3 percentage",
                    values[i * 7 + 6]);
                }
                addGlobalMeta("Mixer 0 - low signal on", values[14]);
                addGlobalMeta("Mixer 1 - low signal on", values[15]);
                break;
              case 8:
                addGlobalMeta("Laser name - laser 1", values[0]);
                break;
              case 9:
                addGlobalMeta("Laser name - laser 2", values[0]);
                break;
              case 10:
                addGlobalMeta("Laser name - laser 3", values[0]);
                break;
              case 11:
                for (int i=0; i<3; i++) {
                  String prefix = "Transmission detector " + (i + 1) + " - ";
                  addGlobalMeta(prefix + "offset", values[i * 3]);
                  addGlobalMeta(prefix + "gain", values[i * 3 + 1]);
                  addGlobalMeta(prefix + "black level", values[i * 3 + 2]);
                  store.setDetectorOffset(new Float(values[i * 3]), 0, i);
                  store.setDetectorGain(new Float(values[i * 3 + 1]), 0, i);
                  store.setDetectorType("Unknown", 0, i);
                }
                break;
              case 12:
                for (int i=0; i<2; i++) {
                  String prefix = "Part number for ";
                  addGlobalMeta(prefix + "laser " + (i + 1), values[i * 4]);
                  addGlobalMeta(prefix + "excitation filter for laser " +
                    (i + 1), values[i * 4 + 1]);
                  addGlobalMeta(prefix + "ND filter for laser " + (i + 1),
                    values[i * 4 + 2]);
                  addGlobalMeta(prefix + "emission filter for laser " + (i + 1),
                    values[i * 4 + 3]);
                }
                break;
              case 13:
                  addGlobalMeta("Part number for laser 3", values[0]);
                  addGlobalMeta("Part number for excitation filter for laser 3",
                    values[1]);
                  addGlobalMeta("Part number for ND filter for laser 3",
                    values[2]);
                  addGlobalMeta("Part number for emission filter for laser 3",
                    values[3]);
                  addGlobalMeta("Part number for filter block 1", values[4]);
                  addGlobalMeta("Part number for filter block 2", values[5]);
                break;
              case 14:
                addGlobalMeta("Filter Block Name - filter block 1", values[0]);
                addGlobalMeta("Filter Block Name - filter block 2", values[1]);
                break;
              case 15:
                for (int i=0; i<5; i++) {
                  addGlobalMeta("Image bands status - band " + (i + 1),
                    values[i*3]);
                  addGlobalMeta("Image bands min - band " + (i + 1),
                    values[i*3 + 1]);
                  addGlobalMeta("Image bands max - band " + (i + 1),
                    values[i*3 + 2]);
                  if (store instanceof IMinMaxStore) {
                    ((IMinMaxStore) store).setChannelGlobalMinMax(i,
                      Double.parseDouble(values[i*3 + 1]),
                      Double.parseDouble(values[i*3 + 2]), 0);
                  }
                }
                break;
              case 17:
                int year = Integer.parseInt(values[5]) + 1900;
                for (int i=0; i<5; i++) {
                  if (values[i].length() == 1) values[i] = "0" + values[i];
                }
                String date = year + "-" + values[4] + "-" + values[3] + "T" +
                  values[2] + ":" + values[1] + ":" + values[0];
                addGlobalMeta("Acquisition date", date);
                store.setImageCreationDate(date, 0);
                break;
              case 18:
                addGlobalMeta("Mixer 3 - enhanced", values[0]);
                for (int i=1; i<=3; i++) {
                  addGlobalMeta("Mixer 3 - PMT " + i + " percentage",
                    values[i]);
                  addGlobalMeta("Mixer 3 - Transmission " + i + " percentage",
                    values[i + 3]);
                  addGlobalMeta("Mixer 3 - photon counting " + i,
                    values[i + 7]);
                }
                addGlobalMeta("Mixer 3 - low signal on", values[7]);
                addGlobalMeta("Mixer 3 - mode", values[11]);
                break;
              case 19:
                for (int i=1; i<=2; i++) {
                  String prefix = "Mixer " + i;
                  addGlobalMeta(prefix + " - photon counting 1",
                    values[i * 4 - 4]);
                  addGlobalMeta(prefix + " - photon counting 2",
                    values[i * 4 - 3]);
                  addGlobalMeta(prefix + " - photon counting 3",
                    values[i * 4 - 2]);
                  addGlobalMeta(prefix + " - mode", values[i * 4 - 1]);
                }
                break;
              case 20:
                addGlobalMeta("Display mode", values[0]);
                addGlobalMeta("Course", values[1]);
                addGlobalMeta("Time Course - experiment type", values[2]);
                addGlobalMeta("Time Course - kd factor", values[3]);
                store.setExperimentType(values[2], 0);
                break;
              case 21:
                addGlobalMeta("Time Course - ion name", values[0]);
                break;
              case 22:
                addGlobalMeta("PIC file generated on Isoscan (lite)",
                  values[0]);
                for (int i=1; i<=3; i++) {
                  addGlobalMeta("Photon counting used (PMT " + i + ")",
                    values[i]);
                  addGlobalMeta("Hot spot filter used (PMT " + i + ")",
                    values[i+3]);
                  addGlobalMeta("Tx Selector used (TX " + i + ")",
                    values[i + 6]);
                }
                break;
            }
          }
          break;
        default:
          addGlobalMeta("Note #" + noteCount, n.toString());
      }
      if (n.p.indexOf("AXIS") != -1) {
        n.p = n.p.replaceAll("=", "");
        Vector v = new Vector();
        StringTokenizer tokens = new StringTokenizer(n.p, " ");
        while (tokens.hasMoreTokens()) {
          String token = tokens.nextToken().trim();
          if (token.length() > 0) v.add(token);
        }
        String[] values = (String[]) v.toArray(new String[0]);
        String key = values[0];
        String noteType = values[1];
        int axisType = Integer.parseInt(noteType);
        if (values.length > 2) {
          switch (axisType) {
            case 1:
              String dx = values[2];
              String dy = values[3];
              addGlobalMeta(key + " distance (X) in microns", dx);
              addGlobalMeta(key + " distance (Y) in microns", dy);
              break;
            case 3:
              addGlobalMeta(key + " angle (X) in degrees", values[2]);
              addGlobalMeta(key + " angle (Y) in degrees", values[3]);
              break;
            case 4:
              addGlobalMeta(key + " intensity (X)", values[2]);
              addGlobalMeta(key + " intensity (Y)", values[3]);
              break;
            case 6:
              addGlobalMeta(key + " ratio (X)", values[2]);
              addGlobalMeta(key + " ratio (Y)", values[3]);
              break;
            case 7:
              addGlobalMeta(key + " log ratio (X)", values[2]);
              addGlobalMeta(key + " log ratio (Y)", values[3]);
              break;
            case 9:
              addGlobalMeta(key + " noncalibrated intensity min", values[2]);
              addGlobalMeta(key + " noncalibrated intensity max", values[3]);
              addGlobalMeta(key + " calibrated intensity min", values[4]);
              addGlobalMeta(key + " calibrated intensity max", values[5]);
              break;
            case 11:
              addGlobalMeta(key + " RGB type (X)", values[2]);
              addGlobalMeta(key + " RGB type (Y)", values[3]);
              if (key.equals("AXIS_4")) {
                core[0].sizeC = getImageCount();
                core[0].sizeZ = 1;
                core[0].sizeT = 1;
              }
              break;
            case 14:
              addGlobalMeta(key + " time course type (X)", values[2]);
              addGlobalMeta(key + " time course type (Y)", values[3]);
              break;
            case 15:
              String prefix = " inverse sigmoid calibrated intensity ";
              addGlobalMeta(key + prefix + "(min)", values[2]);
              addGlobalMeta(key + prefix + "(max)", values[3]);
              addGlobalMeta(key + prefix + "(beta)", values[4]);
              addGlobalMeta(key + prefix + "(Kd)", values[5]);
              break;
            case 16:
              prefix = " log inverse sigmoid calibrated intensity ";
              addGlobalMeta(key + prefix + "(min)", values[2]);
              addGlobalMeta(key + prefix + "(max)", values[3]);
              addGlobalMeta(key + prefix + "(beta)", values[4]);
              addGlobalMeta(key + prefix + "(Kd)", values[5]);
              break;
          }
        }
      }
    }
    MetadataTools.populatePixels(store, this);
    MetadataTools.setDefaultCreationDate(store, id, 0);
    store.setImageName(name, 0);
    store.setInstrumentID("Instrument:0", 0);
    store.setImageInstrumentRef("Instrument:0", 0);
    store.setObjectiveID("Objective:0", 0, 0);
    store.setObjectiveSettingsObjective("Objective:0", 0);
    store.setObjectiveLensNA(new Float(lens), 0, 0);
    store.setObjectiveNominalMagnification(new Integer((int) magFactor), 0, 0);
    store.setObjectiveCorrection("Unknown", 0, 0);
    store.setObjectiveImmersion("Unknown", 0, 0);
  }
