docs: public SpannableStringBuilder build() {
          if (mBeforeStringBuilder.length() > 0) {
              mStringBuilder.insert(0, mBeforeStringBuilder);
  
              // reset SpecialUnit start pos
              if (!mFinalSpecialUnit.isEmpty()) {
                  for (BaseSpecialUnit specialUnit : mFinalSpecialUnit) {
                      int[] tempStartPoss = specialUnit.getStartPoss();
  
                      if (null == tempStartPoss || tempStartPoss.length == 0) continue;
  
                      for (int i = 0; i < tempStartPoss.length; i++) {
                          int oldStartPos = tempStartPoss[i];
                          tempStartPoss[i] = oldStartPos + mBeforeStringBuilder.length();
                      }
                  }
              }
  
              // reset Cache SpecialClickableUnit start pos
              if (!mCacheSpecialClickableUnitMap.isEmpty()) {
                  for (Map.Entry<SpecialClickableUnit, PositionInfo> cm : mCacheSpecialClickableUnitMap.entrySet()) {
                      cm.getValue().startPos += mBeforeStringBuilder.length();
                  }
              }
          }
  
          if (!mBeforeCacheSpecialClickableUnitMap.isEmpty()) {
              mCacheSpecialClickableUnitMap.putAll(mBeforeCacheSpecialClickableUnitMap);
          }
  
          if (!mBeforeSpecialUnit.isEmpty()) {
              mFinalSpecialUnit.addAll(mBeforeSpecialUnit);
          }
  
          if (mStringBuilder.length() == 0) return null;
          if (mFinalSpecialUnit.isEmpty())
              return new SpannableStringBuilder(mStringBuilder.toString());
  
          if (mNormalSizeText.length() == 0) mNormalSizeText.append(mStringBuilder);
  
          String normalSizeText = mNormalSizeText.toString();
          SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(mStringBuilder);
          boolean isInitClickListener = false;
          for (BaseSpecialUnit st : mFinalSpecialUnit) {
              String specialText = st.getText();
              int[] startPoss = st.getStartPoss();
  
              if (TextUtils.isEmpty(specialText) || null == startPoss || startPoss.length == 0)
                  continue;
  
              int specialTextLength = specialText.length();
              if (st instanceof SpecialTextUnit) {
                  // text span
                  SpecialTextUnit specialTextUnit = (SpecialTextUnit) st;
  
                  final SpecialClickableUnit internalSpecialClickableUnit = specialTextUnit.getSpecialClickableUnit();
                  if (null != internalSpecialClickableUnit) {
                      if (internalSpecialClickableUnit.getNormalTextColor() == 0) {
                          internalSpecialClickableUnit.setNormalTextColor(specialTextUnit.getTextColor());
                      }
                      if (internalSpecialClickableUnit.getNormalBgColor() == 0) {
                          internalSpecialClickableUnit.setNormalBgColor(specialTextUnit.getTextBackgroundColor());
                      }
                  }
                  for (int startPos : startPoss) {
                      // Set Text Color
                      if (specialTextUnit.getTextColor() != 0) {
                          spannableStringBuilder.setSpan(new ForegroundColorSpan(specialTextUnit.getTextColor()), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
  
                      // Set Text Background Color
                      if (specialTextUnit.getTextBackgroundColor() != 0 && null == internalSpecialClickableUnit) {
                          spannableStringBuilder.setSpan(new BackgroundColorSpan(specialTextUnit.getTextBackgroundColor()), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
  
                      // Add Underline
                      if (specialTextUnit.isShowUnderline()) {
                          spannableStringBuilder.setSpan(new UnderlineSpan(), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
  
                      // Add StrikeThrough
                      if (specialTextUnit.isShowStrikeThrough()) {
                          spannableStringBuilder.setSpan(new StrikethroughSpan(), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
  
                      // Set Text Bold
                      if (specialTextUnit.isTextBold()) {
                          spannableStringBuilder.setSpan(new StyleSpan(Typeface.BOLD), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
  
                      // Set Text Italic
                      if (specialTextUnit.isTextItalic()) {
                          spannableStringBuilder.setSpan(new StyleSpan(Typeface.ITALIC), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
  
                      // Set Text Style
                      if (specialTextUnit.getTextStyle() != Typeface.NORMAL) {
                          spannableStringBuilder.setSpan(new StyleSpan(specialTextUnit.getTextStyle()), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
  
                      // Set Text Size
                      if (specialTextUnit.getTextSize() > 0) {
                          TextView curTextView = specialTextUnit.getCurTextView();
                          int gravity = specialTextUnit.getGravity();
                          if (gravity != SpecialGravity.BOTTOM && null != curTextView) {
                              spannableStringBuilder.setSpan(new CustomAbsoluteSizeSpan(normalSizeText, specialTextUnit.getText(), Math.round(specialTextUnit.getTextSize()), curTextView, gravity), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                          } else {
                              spannableStringBuilder.setSpan(new AbsoluteSizeSpan(Math.round(specialTextUnit.getTextSize()), true), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                          }
                      }
  
                      // set clickable
                      if (null != internalSpecialClickableUnit) {
                          if (!isInitClickListener) {
                              isInitClickListener = true;
                              TextView curTextView = internalSpecialClickableUnit.getCurTextView();
                              if (null != curTextView) {
                                  curTextView.setMovementMethod(CustomLinkMovementMethod.getInstance());
                              }
                          }
                          spannableStringBuilder.setSpan(new CustomClickableSpan(internalSpecialClickableUnit), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                      }
                  }
              } else if (st instanceof SpecialImageUnit) {
                  // image Span
                  SpecialImageUnit specialImageUnit = (SpecialImageUnit) st;
                  Bitmap bitmap = specialImageUnit.getBitmap();
                  int imgWidth = specialImageUnit.getWidth();
                  int imgHeight = specialImageUnit.getHeight();
                  if (imgWidth > 0 && imgHeight > 0) {
                      int bitWidth = bitmap.getWidth();
                      int bitHeight = bitmap.getHeight();
  
                      if (imgWidth < bitWidth && imgHeight < bitHeight) {
                          Bitmap newBitmap = ThumbnailUtils.extractThumbnail(bitmap, imgWidth, imgHeight);
                          if (null != newBitmap) {
                              bitmap.recycle();
                              specialImageUnit.setBitmap(newBitmap);
                          }
                      }
                  }
  
                  for (int startPos : startPoss) {
                      CustomImageSpan curCustomImageSpan = new CustomImageSpan(normalSizeText, specialImageUnit);
                      spannableStringBuilder.setSpan(curCustomImageSpan, startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  
                      if (specialImageUnit.isClickable()) {
                          addClickStateChangeListeners(startPos, startPos + specialTextLength, curCustomImageSpan);
                      }
                  }
              } else if (st instanceof SpecialLabelUnit) {
                  // label span
                  SpecialLabelUnit specialLabelUnit = (SpecialLabelUnit) st;
                  for (int startPos : startPoss) {
                      CustomLabelSpan curCustomLabelSpan = new CustomLabelSpan(normalSizeText, specialLabelUnit);
                      spannableStringBuilder.setSpan(curCustomLabelSpan, startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  
                      if (specialLabelUnit.isClickable()) {
                          addClickStateChangeListeners(startPos, startPos + specialTextLength, curCustomLabelSpan);
                      }
                  }
              } else if (st instanceof SpecialClickableUnit) {
                  // clickable span
                  SpecialClickableUnit specialClickableUnit = (SpecialClickableUnit) st;
  
                  if (!isInitClickListener) {
                      isInitClickListener = true;
                      TextView curTextView = specialClickableUnit.getCurTextView();
                      if (null != curTextView) {
                          curTextView.setMovementMethod(CustomLinkMovementMethod.getInstance());
                      }
                  }
  
                  int startPos = startPoss[0];
                  spannableStringBuilder.setSpan(new CustomClickableSpan(specialClickableUnit), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
              } else if (st instanceof SpecialRawSpanUnit) {
                  // raw span
                  SpecialRawSpanUnit specialRawSpanUnit = (SpecialRawSpanUnit) st;
  
                  int startPos = startPoss[0];
                  spannableStringBuilder.setSpan(specialRawSpanUnit.getSpanObj(), startPos, startPos + specialTextLength, specialRawSpanUnit.getFlags());
  
                  // Temporarily unable to support all
              }
          }
  
          return spannableStringBuilder;
      }