/**
  * Process the text so that it will render with a combination of fonts if needed.
  *
  * @param text the text
  * @return a <CODE>Phrase</CODE> with one or more chunks
  */
 public Phrase process(String text) {
   int fsize = fonts.size();
   if (fsize == 0) throw new IndexOutOfBoundsException("No font is defined.");
   char cc[] = text.toCharArray();
   int len = cc.length;
   StringBuffer sb = new StringBuffer();
   Font font = null;
   int lastidx = -1;
   Phrase ret = new Phrase();
   for (int k = 0; k < len; ++k) {
     char c = cc[k];
     if (c == '\n' || c == '\r') {
       sb.append(c);
       continue;
     }
     if (Utilities.isSurrogatePair(cc, k)) {
       int u = Utilities.convertToUtf32(cc, k);
       for (int f = 0; f < fsize; ++f) {
         font = (Font) fonts.get(f);
         if (font.getBaseFont().charExists(u)) {
           if (lastidx != f) {
             if (sb.length() > 0 && lastidx != -1) {
               Chunk ck = new Chunk(sb.toString(), (Font) fonts.get(lastidx));
               ret.add(ck);
               sb.setLength(0);
             }
             lastidx = f;
           }
           sb.append(c);
           sb.append(cc[++k]);
           break;
         }
       }
     } else {
       for (int f = 0; f < fsize; ++f) {
         font = (Font) fonts.get(f);
         if (font.getBaseFont().charExists(c)) {
           if (lastidx != f) {
             if (sb.length() > 0 && lastidx != -1) {
               Chunk ck = new Chunk(sb.toString(), (Font) fonts.get(lastidx));
               ret.add(ck);
               sb.setLength(0);
             }
             lastidx = f;
           }
           sb.append(c);
           break;
         }
       }
     }
   }
   if (sb.length() > 0) {
     Chunk ck = new Chunk(sb.toString(), (Font) fonts.get(lastidx == -1 ? 0 : lastidx));
     ret.add(ck);
   }
   return ret;
 }
  /**
   * Truncates this <CODE>PdfChunk</CODE> if it's too long for the given width.
   *
   * <p>Returns <VAR>null</VAR> if the <CODE>PdfChunk</CODE> wasn't truncated.
   *
   * @param width a given width
   * @return the <CODE>PdfChunk</CODE> that doesn't fit into the width.
   */
  PdfChunk truncate(float width) {
    if (image != null) {
      if (image.getScaledWidth() > width) {
        PdfChunk pc = new PdfChunk("", this);
        value = "";
        attributes.remove(Chunk.IMAGE);
        image = null;
        font = PdfFont.getDefaultFont();
        return pc;
      } else return null;
    }

    int currentPosition = 0;
    float currentWidth = 0;

    // it's no use trying to split if there isn't even enough place for a space
    if (width < font.width()) {
      String returnValue = value.substring(1);
      value = value.substring(0, 1);
      PdfChunk pc = new PdfChunk(returnValue, this);
      return pc;
    }

    // loop over all the characters of a string
    // or until the totalWidth is reached
    int length = value.length();
    boolean surrogate = false;
    char character;
    while (currentPosition < length) {
      // the width of every character is added to the currentWidth
      surrogate = Utilities.isSurrogatePair(value, currentPosition);
      if (surrogate) currentWidth += font.width(Utilities.convertToUtf32(value, currentPosition));
      else currentWidth += font.width(value.charAt(currentPosition));
      if (currentWidth > width) break;
      if (surrogate) currentPosition++;
      currentPosition++;
    }

    // if all the characters fit in the total width, null is returned (there is no overflow)
    if (currentPosition == length) {
      return null;
    }

    // otherwise, the string has to be truncated
    // currentPosition -= 2;
    // we have to chop off minimum 1 character from the chunk
    if (currentPosition == 0) {
      currentPosition = 1;
      if (surrogate) ++currentPosition;
    }
    String returnValue = value.substring(currentPosition);
    value = value.substring(0, currentPosition);
    PdfChunk pc = new PdfChunk(returnValue, this);
    return pc;
  }
  /**
   * Splits this <CODE>PdfChunk</CODE> if it's too long for the given width.
   *
   * <p>Returns <VAR>null</VAR> if the <CODE>PdfChunk</CODE> wasn't truncated.
   *
   * @param width a given width
   * @return the <CODE>PdfChunk</CODE> that doesn't fit into the width.
   */
  PdfChunk split(float width) {
    newlineSplit = false;
    if (image != null) {
      if (image.getScaledWidth() > width) {
        PdfChunk pc = new PdfChunk(Chunk.OBJECT_REPLACEMENT_CHARACTER, this);
        value = "";
        attributes = new HashMap();
        image = null;
        font = PdfFont.getDefaultFont();
        return pc;
      } else return null;
    }
    HyphenationEvent hyphenationEvent = (HyphenationEvent) noStroke.get(Chunk.HYPHENATION);
    int currentPosition = 0;
    int splitPosition = -1;
    float currentWidth = 0;

    // loop over all the characters of a string
    // or until the totalWidth is reached
    int lastSpace = -1;
    float lastSpaceWidth = 0;
    int length = value.length();
    char valueArray[] = value.toCharArray();
    char character = 0;
    BaseFont ft = font.getFont();
    boolean surrogate = false;
    if (ft.getFontType() == BaseFont.FONT_TYPE_CJK && ft.getUnicodeEquivalent(' ') != ' ') {
      while (currentPosition < length) {
        // the width of every character is added to the currentWidth
        char cidChar = valueArray[currentPosition];
        character = (char) ft.getUnicodeEquivalent(cidChar);
        // if a newLine or carriageReturn is encountered
        if (character == '\n') {
          newlineSplit = true;
          String returnValue = value.substring(currentPosition + 1);
          value = value.substring(0, currentPosition);
          if (value.length() < 1) {
            value = "\u0001";
          }
          PdfChunk pc = new PdfChunk(returnValue, this);
          return pc;
        }
        currentWidth += font.width(cidChar);
        if (character == ' ') {
          lastSpace = currentPosition + 1;
          lastSpaceWidth = currentWidth;
        }
        if (currentWidth > width) break;
        // if a split-character is encountered, the splitPosition is altered
        if (splitCharacter.isSplitCharacter(0, currentPosition, length, valueArray, thisChunk))
          splitPosition = currentPosition + 1;
        currentPosition++;
      }
    } else {
      while (currentPosition < length) {
        // the width of every character is added to the currentWidth
        character = valueArray[currentPosition];
        // if a newLine or carriageReturn is encountered
        if (character == '\r' || character == '\n') {
          newlineSplit = true;
          int inc = 1;
          if (character == '\r'
              && currentPosition + 1 < length
              && valueArray[currentPosition + 1] == '\n') inc = 2;
          String returnValue = value.substring(currentPosition + inc);
          value = value.substring(0, currentPosition);
          if (value.length() < 1) {
            value = " ";
          }
          PdfChunk pc = new PdfChunk(returnValue, this);
          return pc;
        }
        surrogate = Utilities.isSurrogatePair(valueArray, currentPosition);
        if (surrogate)
          currentWidth +=
              font.width(
                  Utilities.convertToUtf32(
                      valueArray[currentPosition], valueArray[currentPosition + 1]));
        else currentWidth += font.width(character);
        if (character == ' ') {
          lastSpace = currentPosition + 1;
          lastSpaceWidth = currentWidth;
        }
        if (surrogate) currentPosition++;
        if (currentWidth > width) break;
        // if a split-character is encountered, the splitPosition is altered
        if (splitCharacter.isSplitCharacter(0, currentPosition, length, valueArray, null))
          splitPosition = currentPosition + 1;
        currentPosition++;
      }
    }

    // if all the characters fit in the total width, null is returned (there is no overflow)
    if (currentPosition == length) {
      return null;
    }
    // otherwise, the string has to be truncated
    if (splitPosition < 0) {
      String returnValue = value;
      value = "";
      PdfChunk pc = new PdfChunk(returnValue, this);
      return pc;
    }
    if (lastSpace > splitPosition && splitCharacter.isSplitCharacter(0, 0, 1, singleSpace, null))
      splitPosition = lastSpace;
    if (hyphenationEvent != null && lastSpace >= 0 && lastSpace < currentPosition) {
      int wordIdx = getWord(value, lastSpace);
      if (wordIdx > lastSpace) {
        String pre =
            hyphenationEvent.getHyphenatedWordPre(
                value.substring(lastSpace, wordIdx),
                font.getFont(),
                font.size(),
                width - lastSpaceWidth);
        String post = hyphenationEvent.getHyphenatedWordPost();
        if (pre.length() > 0) {
          String returnValue = post + value.substring(wordIdx);
          value = trim(value.substring(0, lastSpace) + pre);
          PdfChunk pc = new PdfChunk(returnValue, this);
          return pc;
        }
      }
    }
    String returnValue = value.substring(splitPosition);
    value = trim(value.substring(0, splitPosition));
    PdfChunk pc = new PdfChunk(returnValue, this);
    return pc;
  }
 /**
  * Converts the text into bytes to be placed in the document. The conversion is done according to
  * the font and the encoding and the characters used are stored.
  *
  * @param text the text to convert
  * @return the conversion
  */
 byte[] convertToBytes(final String text) {
   byte b[] = null;
   switch (this.fontType) {
     case BaseFont.FONT_TYPE_T3:
       return this.baseFont.convertToBytes(text);
     case BaseFont.FONT_TYPE_T1:
     case BaseFont.FONT_TYPE_TT:
       {
         b = this.baseFont.convertToBytes(text);
         final int len = b.length;
         for (int k = 0; k < len; ++k) {
           this.shortTag[b[k] & 0xff] = 1;
         }
         break;
       }
     case BaseFont.FONT_TYPE_CJK:
       {
         final int len = text.length();
         for (int k = 0; k < len; ++k) {
           this.cjkTag.put(this.cjkFont.getCidCode(text.charAt(k)), 0);
         }
         b = this.baseFont.convertToBytes(text);
         break;
       }
     case BaseFont.FONT_TYPE_DOCUMENT:
       {
         b = this.baseFont.convertToBytes(text);
         break;
       }
     case BaseFont.FONT_TYPE_TTUNI:
       {
         try {
           int len = text.length();
           int metrics[] = null;
           final char glyph[] = new char[len];
           int i = 0;
           if (this.symbolic) {
             b = PdfEncodings.convertToBytes(text, "symboltt");
             len = b.length;
             for (int k = 0; k < len; ++k) {
               metrics = this.ttu.getMetricsTT(b[k] & 0xff);
               if (metrics == null) {
                 continue;
               }
               this.longTag.put(
                   new Integer(metrics[0]),
                   new int[] {
                     metrics[0], metrics[1], this.ttu.getUnicodeDifferences(b[k] & 0xff)
                   });
               glyph[i++] = (char) metrics[0];
             }
           } else {
             for (int k = 0; k < len; ++k) {
               int val;
               if (Utilities.isSurrogatePair(text, k)) {
                 val = Utilities.convertToUtf32(text, k);
                 k++;
               } else {
                 val = text.charAt(k);
               }
               metrics = this.ttu.getMetricsTT(val);
               if (metrics == null) {
                 continue;
               }
               final int m0 = metrics[0];
               final Integer gl = new Integer(m0);
               if (!this.longTag.containsKey(gl)) {
                 this.longTag.put(gl, new int[] {m0, metrics[1], val});
               }
               glyph[i++] = (char) m0;
             }
           }
           final String s = new String(glyph, 0, i);
           b = s.getBytes(CJKFont.CJK_ENCODING);
         } catch (final UnsupportedEncodingException e) {
           throw new ExceptionConverter(e);
         }
         break;
       }
   }
   return b;
 }