public void setJavascriptForObject(
      final FormObject formObject, final int parentType, final int actionType) {
    final String JSscript;

    final PdfObject actionObj;
    final PdfObject JSobj;

    final PdfObject additionalObject = formObject.getDictionary(parentType);

    final ObjectDecoder objectDecoder = new ObjectDecoder(this.objectReader);
    objectDecoder.checkResolved(additionalObject);

    if (additionalObject == null) {
      return;
    }

    if (actionType == parentType) {
      actionObj = additionalObject;
    } else {
      if (actionType == PdfDictionary.C2) // special case
      {
        actionObj = additionalObject.getDictionary(PdfDictionary.C);
      } else {
        actionObj = additionalObject.getDictionary(actionType);
      }
    }

    if (actionObj == null) {
      //				throw new RuntimeException("Failed on actionType="+actionType+"
      // "+formObject.getPDFRef()+
      //						"\nformObject="+formObject+" parentObject="+additionalObject+
      //						"\nadditionalObject="+additionalObject.getTextStreamValue(PdfDictionary.T)
      //						+"\nadditionalObject="+formObject.getTextStreamValue(PdfDictionary.T));
    } else {

      objectDecoder.checkResolved(actionObj);

      JSobj = actionObj.getDictionary(PdfDictionary.JS);

      if (JSobj != null) {

        final byte[] data = JSobj.getDecodedStream();
        JSscript = StringUtils.getTextString(data, true);

      } else {
        JSscript = actionObj.getTextStreamValue(PdfDictionary.JS);
      }

      // store
      if (JSscript != null) {
        // use name to reference Js if name is null use ref. seems to be slower, but better on
        // abacus/L295KantoonVaadt.pdf
        String name = formObject.getTextStreamValue(PdfDictionary.T);
        if (name == null) {
          name = formObject.getObjectRefAsString();
        }
        javascript.storeJavascript(name, JSscript, actionType);

        // old version
        //
        // javascript.storeJavascript(formObject.getObjectRefAsString(),JSscript,actionType);
      }
    }
  }
  public static int setNameStringValue(
      final PdfObject pdfObject,
      int i,
      final byte[] raw,
      final boolean isMap,
      final Object PDFkey,
      final int PDFkeyInt,
      final PdfFileReader objectReader) {

    byte[] stringBytes;

    // move cursor to end of last command if needed
    while (raw[i] != 10
        && raw[i] != 13
        && raw[i] != 32
        && raw[i] != 47
        && raw[i] != '('
        && raw[i] != '<') {
      i++;
    }

    i = StreamReaderUtils.skipSpaces(raw, i);

    // work out if direct (ie /String or read ref 27 0 R
    int j2 = i;
    byte[] arrayData = raw;

    boolean isIndirect = raw[i] != 47 && raw[i] != 40 && raw[i] != 60; // Some /NAME values start (

    final boolean startsWithBrace = raw[i] == 40;

    // delete
    // @speed - lose this code once Filters done properly
    /*
     * just check its not /Filter [/FlateDecode ] or [] or [ /ASCII85Decode /FlateDecode ]
     * by checking next valid char not /
     */
    boolean isInsideArray = false;
    if (isIndirect) {
      int aa = i + 1;
      aa = StreamReaderUtils.skipSpaces(raw, aa);

      if (raw[aa] == 47 || raw[aa] == ']') {
        isIndirect = false;
        i = aa + 1;
        isInsideArray = true;
      }
    }

    if (isIndirect) { // its in another object so we need to fetch

      final int[] values = StreamReaderUtils.readRefFromStream(raw, i);
      final int ref = values[0];
      final int generation = values[1];
      i = values[2];

      if (raw[i] != 82) { // we are expecting R to end ref
        throw new RuntimeException(
            padding + "2. Unexpected value in file - please send to IDRsolutions for analysis");
      }

      // read the Dictionary data
      arrayData =
          objectReader.readObjectAsByteArray(
              pdfObject, objectReader.isCompressed(ref, generation), ref, generation);

      // allow for data in Linear object not yet loaded
      if (arrayData == null) {
        pdfObject.setFullyResolved(false);

        if (debugFastCode) {
          System.out.println(padding + "Data not yet loaded");
        }

        return raw.length;
      }

      // lose obj at start and roll onto /
      if (arrayData[0] == 47) {
        j2 = 0;
      } else {
        j2 = 3;

        while (arrayData[j2] != 47) {
          j2++;
        }
      }
    }

    // lose /
    j2++;

    // allow for no value with /Intent//Filter
    if (arrayData[j2] == 47) {
      return j2 - 1;
    }

    int end = j2 + 1;

    if (isInsideArray) { // values inside []

      // move cursor to start of text
      j2 = StreamReaderUtils.skipSpacesOrOtherCharacter(arrayData, j2, 47);

      int slashes = 0;

      // count chars
      byte lastChar = 0;
      while (true) {

        if (arrayData[end] == ']') {
          break;
        }

        if (arrayData[end] == 47
            && (lastChar == 32 || lastChar == 10 || lastChar == 13)) // count the / if gap before
        {
          slashes++;
        }

        lastChar = arrayData[end];
        end++;

        if (end == arrayData.length) {
          break;
        }
      }

      // set value and ensure space gap
      final int charCount = end - slashes;
      int ptr = 0;
      stringBytes = new byte[charCount - j2];

      byte nextChar, previous = 0;
      for (int ii = j2; ii < charCount; ii++) {
        nextChar = arrayData[ii];
        if (nextChar == 47) {
          if (previous != 32 && previous != 10 && previous != 13) {
            stringBytes[ptr] = 32;
            ptr++;
          }
        } else {
          stringBytes[ptr] = nextChar;
          ptr++;
        }

        previous = nextChar;
      }
    } else { // its in data stream directly or (string)

      // count chars
      while (true) {

        if (startsWithBrace) {
          if (arrayData[end] == ')' && !ObjectUtils.isEscaped(arrayData, end)) {
            break;
          }
        } else if (arrayData[end] == 32
            || arrayData[end] == 10
            || arrayData[end] == 13
            || arrayData[end] == 47
            || arrayData[end] == 62) {
          break;
        }

        end++;

        if (end == arrayData.length) {
          break;
        }
      }

      // set value
      final int charCount = end - j2;
      stringBytes = new byte[charCount];
      System.arraycopy(arrayData, j2, stringBytes, 0, charCount);
    }

    if (isMap) {
      pdfObject.setName(PDFkey, StringUtils.getTextString(stringBytes, false));
    } else {
      pdfObject.setName(PDFkeyInt, stringBytes);
    }

    if (debugFastCode) {
      System.out.println(
          padding + "String set as =" + new String(stringBytes) + "< written to " + pdfObject);
    }

    // put cursor in correct place (already there if ref)
    if (!isIndirect) {
      i = end - 1;
    }

    return i;
  }
  /** for the use of the printf commend */
  private static String convertToken(final String token, final String arg1) {

    if (!StringUtils.isNumber(arg1)) {
      return "";
    }

    final byte[] bytes = StringUtils.toBytes(arg1);
    final double value = NumberUtils.parseDouble(0, bytes.length, bytes);

    // a seperator is used for dates, phone numbers, times, ete
    int decimalPoints = -1, minWidth = 0;
    char decimal = '.'; // seperator = ',',
    boolean padd = false, floatDecimal = false;
    final StringBuilder sValue =
        new StringBuilder(); // used to store the value in string form for each type of output
    final StringBuilder returnString = new StringBuilder();

    // conversion token layout: %[,nDecSep][cFlags][nWidth][.nPrecision]cConvChar
    final char[] tokArray = token.toCharArray();
    if (tokArray[0] == '%') {
      int i = 1;
      final int size = tokArray.length;
      loop:
      while (i < size) {
        switch (tokArray[i]) {
          case ',':
            //					nDecSep - A comma character (,) followed by a digit that indicates the
            // decimal/separator format:
            switch (tokArray[++i]) {
              case '0':
                //						0 � Comma separated, period decimal point
                // seperator = ',';
                decimal = '.';
                break;
              case '1':
                //						1 � No separator, period decimal point
                // seperator = 0;
                decimal = '.';
                break;
              case '2':
                //						2 � Period separated, comma decimal point
                // seperator = '.';
                decimal = ',';
                break;
              case '3':
                //						3 � No separator, comma decimal point
                // seperator = 0;
                decimal = ',';
                break;
            }
            break;

            //				cFlags - Only valid for numeric conversions and consists of a number of characters
            // (in any order),
            //				which will modify the specification:
          case '+': // cFlags
            //					+ � Specifies that the number will always be formatted with a sign.
            if (value > 0) {
              returnString.append('+');
            } else {
              returnString.append('-');
            }
            break;
          case ' ': // cFlags
            //					space � If the first character is not a sign, a space will be prefixed.
            if (value > 0) {
              returnString.append(' ');
            } else {
              returnString.append('-');
            }
            break;
          case '0': // cFlags
            //					0 � Specifies padding to the field with leading zeros.
            padd = true;
            break;
          case '#': // cFlags
            //					# � Specifies an alternate output form. For f, the output will always have a
            // decimal point.
            floatDecimal = true;
            break;

          case '.':
            //					nPrecision - A period character (.) followed by a number that specifies the
            // number of digits
            //					after the decimal point for float conversions.
            decimalPoints = Integer.parseInt(String.valueOf(tokArray[++i]));
            break;

          case 'd': // cConvChar
            //				d � Integer (truncating if necessary)
            sValue.append((int) value);
            if (padd) {
              final int stringlen = returnString.length() + sValue.length();
              if (stringlen < minWidth) {
                for (int p = 0; p < minWidth - stringlen; p++) {
                  returnString.append('0');
                }
              }
            }
            returnString.append(sValue);
            break loop;

          case 'f': // cConvChar
            //				f � Floating-point number
            if (decimalPoints != -1) {
              if (decimalPoints == 0) {
                sValue.append((int) value);
              } else {
                final NumberFormat nf = NumberFormat.getInstance();
                nf.setMinimumFractionDigits(decimalPoints);
                nf.setMaximumFractionDigits(decimalPoints);
                sValue.append(nf.format(value));
              }
            } else {
              sValue.append((float) value);
            }

            if (floatDecimal && sValue.indexOf(".") != -1) {
              sValue.append('.');
            }
            if (padd) {
              final int stringlen = returnString.length() + sValue.length();
              if (stringlen < minWidth) {
                for (int p = 0; p < minWidth - stringlen; p++) {
                  returnString.append('0');
                }
              }
            }
            String ssVal = sValue.toString();
            ssVal = ssVal.replace('.', decimal); // replace the decimal point with the defined one
            returnString.append(ssVal);
            break loop;

          case 's': // cConvChar
            //				s � String
            sValue.append(value); // amay need arg1
            if (padd) {
              final int stringlen = returnString.length() + sValue.length();
              if (stringlen < minWidth) {
                for (int p = 0; p < minWidth - stringlen; p++) {
                  returnString.append('0');
                }
              }
            }
            returnString.append(sValue);
            break loop;

          case 'x': // cConvChar
            //				x � Integer (truncating if necessary) and formatted in unsigned hexadecimal
            // notation
            final int valI = (int) (value);
            final String retValS = Integer.toHexString(valI);

            sValue.append(retValS);
            if (padd) {
              final int stringlen = returnString.length() + sValue.length();
              if (stringlen < minWidth) {
                for (int p = 0; p < minWidth - stringlen; p++) {
                  returnString.append('0');
                }
              }
            }
            returnString.append(sValue);
            break loop;

          default:
            //					nWidth - A number specifying a minimum field width. The converted argument is
            // formatted to be at
            //					least this many characters wide, including the sign and decimal point, and may be
            // wider
            //					if necessary. If the converted argument has fewer characters than the field
            // width, it is
            //					padded on the left to make up the field width. The padding character is normally
            // a space,
            //					but is 0 if the zero padding flag is present (cFlags contains 0).
            minWidth = NumberUtils.parseInt(0, 1, new byte[] {(byte) tokArray[i]});
            break;
        }
        i++;
      } // end while loop
    }

    return returnString.toString();
  }