public EmbeddedObjectRefSubRecord(LittleEndianInput in, int size) {

    // Much guess-work going on here due to lack of any documentation.
    // See similar source code in OOO:
    // http://svn.services.openoffice.org/ooo/trunk/sc/source/filter/excel/xiescher.cxx
    // 1223 void XclImpOleObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nRecSize )

    int streamIdOffset = in.readShort(); // OOO calls this 'nFmlaLen'
    int remaining = size - LittleEndian.SHORT_SIZE;

    int dataLenAfterFormula = remaining - streamIdOffset;
    int formulaSize = in.readUShort();
    remaining -= LittleEndian.SHORT_SIZE;
    field_1_unknown_int = in.readInt();
    remaining -= LittleEndian.INT_SIZE;
    byte[] formulaRawBytes = readRawData(in, formulaSize);
    remaining -= formulaSize;
    field_2_refPtg = readRefPtg(formulaRawBytes);
    if (field_2_refPtg == null) {
      // common case
      // field_2_n16 seems to be 5 here
      // The formula almost looks like tTbl but the row/column values seem like garbage.
      field_2_unknownFormulaData = formulaRawBytes;
    } else {
      field_2_unknownFormulaData = null;
    }

    int stringByteCount;
    if (remaining >= dataLenAfterFormula + 3) {
      int tag = in.readByte();
      stringByteCount = LittleEndian.BYTE_SIZE;
      if (tag != 0x03) {
        throw new RecordFormatException("Expected byte 0x03 here");
      }
      int nChars = in.readUShort();
      stringByteCount += LittleEndian.SHORT_SIZE;
      if (nChars > 0) {
        // OOO: the 4th way Xcl stores a unicode string: not even a Grbit byte present if length 0
        field_3_unicode_flag = (in.readByte() & 0x01) != 0;
        stringByteCount += LittleEndian.BYTE_SIZE;
        if (field_3_unicode_flag) {
          field_4_ole_classname = StringUtil.readUnicodeLE(in, nChars);
          stringByteCount += nChars * 2;
        } else {
          field_4_ole_classname = StringUtil.readCompressedUnicode(in, nChars);
          stringByteCount += nChars;
        }
      } else {
        field_4_ole_classname = "";
      }
    } else {
      field_4_ole_classname = null;
      stringByteCount = 0;
    }
    remaining -= stringByteCount;
    // Pad to next 2-byte boundary
    if (((stringByteCount + formulaSize) % 2) != 0) {
      int b = in.readByte();
      remaining -= LittleEndian.BYTE_SIZE;
      if (field_2_refPtg != null && field_4_ole_classname == null) {
        field_4_unknownByte = Byte.valueOf((byte) b);
      }
    }
    int nUnexpectedPadding = remaining - dataLenAfterFormula;

    if (nUnexpectedPadding > 0) {
      logger.log(
          POILogger.ERROR, "Discarding " + nUnexpectedPadding + " unexpected padding bytes ");
      readRawData(in, nUnexpectedPadding);
      remaining -= nUnexpectedPadding;
    }

    // Fetch the stream ID
    if (dataLenAfterFormula >= 4) {
      field_5_stream_id = Integer.valueOf(in.readInt());
      remaining -= LittleEndian.INT_SIZE;
    } else {
      field_5_stream_id = null;
    }
    field_6_unknown = readRawData(in, remaining);
  }
  public HyperlinkRecord(RecordInputStream in) {
    _range = new CellRangeAddress(in);

    _guid = new GUID(in);

    /**
     * streamVersion (4 bytes): An unsigned integer that specifies the version number of the
     * serialization implementation used to save this structure. This value MUST equal 2.
     */
    int streamVersion = in.readInt();
    if (streamVersion != 0x00000002) {
      throw new RecordFormatException("Stream Version must be 0x2 but found " + streamVersion);
    }
    _linkOpts = in.readInt();

    if ((_linkOpts & HLINK_LABEL) != 0) {
      int label_len = in.readInt();
      _label = in.readUnicodeLEString(label_len);
    }

    if ((_linkOpts & HLINK_TARGET_FRAME) != 0) {
      int len = in.readInt();
      _targetFrame = in.readUnicodeLEString(len);
    }

    if ((_linkOpts & HLINK_URL) != 0 && (_linkOpts & HLINK_UNC_PATH) != 0) {
      _moniker = null;
      int nChars = in.readInt();
      _address = in.readUnicodeLEString(nChars);
    }

    if ((_linkOpts & HLINK_URL) != 0 && (_linkOpts & HLINK_UNC_PATH) == 0) {
      _moniker = new GUID(in);

      if (URL_MONIKER.equals(_moniker)) {
        int length = in.readInt();
        /**
         * The value of <code>length<code> be either the byte size of the url field
         * (including the terminating NULL character) or the byte size of the url field plus 24.
         * If the value of this field is set to the byte size of the url field,
         * then the tail bytes fields are not present.
         */
        int remaining = in.remaining();
        if (length == remaining) {
          int nChars = length / 2;
          _address = in.readUnicodeLEString(nChars);
        } else {
          int nChars = (length - TAIL_SIZE) / 2;
          _address = in.readUnicodeLEString(nChars);
          /**
           * TODO: make sense of the remaining bytes According to the spec they consist of: 1.
           * 16-byte GUID: This field MUST equal {0xF4815879, 0x1D3B, 0x487F, 0xAF, 0x2C, 0x82,
           * 0x5D, 0xC4, 0x85, 0x27, 0x63} 2. Serial version, this field MUST equal 0 if present. 3.
           * URI Flags
           */
          _uninterpretedTail = readTail(URL_TAIL, in);
        }
      } else if (FILE_MONIKER.equals(_moniker)) {
        _fileOpts = in.readShort();

        int len = in.readInt();
        _shortFilename = StringUtil.readCompressedUnicode(in, len);
        _uninterpretedTail = readTail(FILE_TAIL, in);
        int size = in.readInt();
        if (size > 0) {
          int charDataSize = in.readInt();

          // From the spec: An optional unsigned integer that MUST be 3 if present
          // but some files has 4
          int usKeyValue = in.readUShort();

          _address = StringUtil.readUnicodeLE(in, charDataSize / 2);
        } else {
          _address = null;
        }
      } else if (STD_MONIKER.equals(_moniker)) {
        _fileOpts = in.readShort();

        int len = in.readInt();

        byte[] path_bytes = new byte[len];
        in.readFully(path_bytes);

        _address = new String(path_bytes);
      }
    }

    if ((_linkOpts & HLINK_PLACE) != 0) {

      int len = in.readInt();
      _textMark = in.readUnicodeLEString(len);
    }

    if (in.remaining() > 0) {
      logger.log(
          POILogger.WARN,
          "Hyperlink data remains: " + in.remaining() + " : " + HexDump.toHex(in.readRemainder()));
    }
  }