Open Source Repository

Home /itextpdf/itextpdf-5.1.2 | Repository Home


com/itextpdf/text/html/simpleparser/ElementFactory.java
/*
 * $Id: FactoryProperties.java 4610 2010-11-02 17:28:50Z blowagie $
 *
 * This file is part of the iText (R) project.
 * Copyright (c) 1998-2011 1T3XT BVBA
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: [email protected]
 */
package com.itextpdf.text.html.simpleparser;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.DocListener;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.FontProvider;
import com.itextpdf.text.Image;
import com.itextpdf.text.List;
import com.itextpdf.text.ListItem;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.html.HtmlTags;
import com.itextpdf.text.html.HtmlUtilities;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.HyphenationAuto;
import com.itextpdf.text.pdf.HyphenationEvent;
import com.itextpdf.text.pdf.draw.LineSeparator;
/**
 * Factory that produces iText Element objects,
 * based on tags and their properties.
 @author blowagie
 @author psoares
 @since 5.0.6 (renamed)
 */
public class ElementFactory {



  /**
   * The font provider that will be used to fetch fonts.
   @since  iText 5.0  This used to be a FontFactoryImp
   */
  private FontProvider provider = FontFactory.getFontImp();

  /**
   * Creates a new instance of FactoryProperties.
   */
  public ElementFactory() {
  }

  /**
   * Setter for the font provider
   @param provider
   @since  5.0.6 renamed from setFontImp
   */
  public void setFontProvider(final FontProvider provider) {
    this.provider = provider;
  }

  /**
   * Getter for the font provider
   @return provider
   @since 5.0.6 renamed from getFontImp
   */
  public FontProvider getFontProvider() {
    return provider;
  }

  /**
   * Creates a Font object based on a chain of properties.
   @param  chain  chain of properties
   @return  an iText Font object
   */
  public Font getFont(final ChainedProperties chain) {

    // [1] font name

    String face = chain.getProperty(HtmlTags.FACE);
    // try again, under the CSS key.
    //ISSUE: If both are present, we always go with face, even if font-family was
    //  defined more recently in our ChainedProperties.  One solution would go like this:
    //    Map all our supported style attributes to the 'normal' tag name, so we could
    //    look everything up under that one tag, retrieving the most current value.
    if (face == null || face.trim().length() == 0) {
      face = chain.getProperty(HtmlTags.FONTFAMILY);
    }
    // if the font consists of a comma separated list,
    // take the first font that is registered
    if (face != null) {
      StringTokenizer tok = new StringTokenizer(face, ",");
      while (tok.hasMoreTokens()) {
        face = tok.nextToken().trim();
        if (face.startsWith("\""))
          face = face.substring(1);
        if (face.endsWith("\""))
          face = face.substring(0, face.length() 1);
        if (provider.isRegistered(face))
          break;
      }
    }

    // [2] encoding
    String encoding = chain.getProperty(HtmlTags.ENCODING);
    if (encoding == null)
      encoding = BaseFont.WINANSI;

    // [3] embedded

    // [4] font size
    String value = chain.getProperty(HtmlTags.SIZE);
    float size = 12;
    if (value != null)
      size = Float.parseFloat(value);

    // [5] font style
    int style = 0;

    // text-decoration
    String decoration = chain.getProperty(HtmlTags.TEXTDECORATION);
    if (decoration != null && decoration.trim().length() != 0) {
      if (HtmlTags.UNDERLINE.equals(decoration)) {
        style |= Font.UNDERLINE;
      else if (HtmlTags.LINETHROUGH.equals(decoration)) {
        style |= Font.STRIKETHRU;
      }
    }
    // italic
    if (chain.hasProperty(HtmlTags.I))
      style |= Font.ITALIC;
    // bold
    if (chain.hasProperty(HtmlTags.B))
      style |= Font.BOLD;
    // underline
    if (chain.hasProperty(HtmlTags.U))
      style |= Font.UNDERLINE;
    // strikethru
    if (chain.hasProperty(HtmlTags.S))
      style |= Font.STRIKETHRU;

    // [6] Color
    BaseColor color = HtmlUtilities.decodeColor(chain.getProperty(HtmlTags.COLOR));

    // Get the font object from the provider
    return provider.getFont(face, encoding, true, size, style, color);
  }


  /**
   * Creates an iText Chunk
   @param content the content of the Chunk
   @param chain the hierarchy chain
   @return a Chunk
   */
  public Chunk createChunk(final String content, final ChainedProperties chain) {
    Font font = getFont(chain);
    Chunk ck = new Chunk(content, font);
    if (chain.hasProperty(HtmlTags.SUB))
      ck.setTextRise(-font.getSize() 2);
    else if (chain.hasProperty(HtmlTags.SUP))
      ck.setTextRise(font.getSize() 2);
    ck.setHyphenation(getHyphenation(chain));
    return ck;
  }

  /**
   * Creates an iText Paragraph object using the properties
   * of the different tags and properties in the hierarchy chain.
   @param  chain  the hierarchy chain
   @return  a Paragraph without any content
   */
  public Paragraph createParagraph(final ChainedProperties chain) {
    Paragraph paragraph = new Paragraph();
    updateElement(paragraph, chain);
    return paragraph;
  }

  /**
   * Creates an iText Paragraph object using the properties
   * of the different tags and properties in the hierarchy chain.
   @param  chain  the hierarchy chain
   @return  a ListItem without any content
   */
  public ListItem createListItem(final ChainedProperties chain) {
    ListItem item = new ListItem();
    updateElement(item, chain);
    return item;
  }

  /**
   * Method that does the actual Element creating for
   * the createParagraph and createListItem method.
   @param paragraph
   @param chain
   */
  protected void updateElement(final Paragraph paragraph, final ChainedProperties chain) {
    // Alignment
    String value = chain.getProperty(HtmlTags.ALIGN);
    paragraph.setAlignment(HtmlUtilities.alignmentValue(value));
    // hyphenation
    paragraph.setHyphenation(getHyphenation(chain));
    // leading
    setParagraphLeading(paragraph, chain.getProperty(HtmlTags.LEADING));
    // spacing before
    value = chain.getProperty(HtmlTags.AFTER);
    if (value != null) {
      try {
        paragraph.setSpacingBefore(Float.parseFloat(value));
      catch (Exception e) {
      }
    }
    // spacing after
    value = chain.getProperty(HtmlTags.AFTER);
    if (value != null) {
      try {
        paragraph.setSpacingAfter(Float.parseFloat(value));
      catch (Exception e) {
      }
    }
    // extra paragraph space
    value = chain.getProperty(HtmlTags.EXTRAPARASPACE);
    if (value != null) {
      try {
        paragraph.setExtraParagraphSpace(Float.parseFloat(value));
      catch (Exception e) {
      }
    }
    // indentation
    value = chain.getProperty(HtmlTags.INDENT);
    if (value != null) {
      try {
        paragraph.setIndentationLeft(Float.parseFloat(value));
      catch (Exception e) {
      }
    }
  }

  /**
   * Sets the leading of a Paragraph object.
   @param  paragraph  the Paragraph for which we set the leading
   @param  leading    the String value of the leading
   */
  protected static void setParagraphLeading(final Paragraph paragraph, final String leading) {
    // default leading
    if (leading == null) {
      paragraph.setLeading(01.5f);
      return;
    }
    try {
      StringTokenizer tk = new StringTokenizer(leading, " ,");
      // absolute leading
      String v = tk.nextToken();
      float v1 = Float.parseFloat(v);
      if (!tk.hasMoreTokens()) {
        paragraph.setLeading(v1, 0);
        return;
      }
      // relative leading
      v = tk.nextToken();
      float v2 = Float.parseFloat(v);
      paragraph.setLeading(v1, v2);
    catch (Exception e) {
      // default leading
      paragraph.setLeading(01.5f);
    }
  }


  /**
   * Gets a HyphenationEvent based on the hyphenation entry in
   * the hierarchy chain.
   @param  chain  the hierarchy chain
   @return  a HyphenationEvent
   @since  2.1.2
   */
  public HyphenationEvent getHyphenation(final ChainedProperties chain) {
    String value = chain.getProperty(HtmlTags.HYPHENATION);
    // no hyphenation defined
    if (value == null || value.length() == 0) {
      return null;
    }
    // language code only
    int pos = value.indexOf('_');
    if (pos == -1) {
      return new HyphenationAuto(value, null, 22);
    }
    // language and country code
    String lang = value.substring(0, pos);
    String country = value.substring(pos + 1);
    // no leftMin or rightMin
    pos = country.indexOf(',');
    if (pos == -1) {
      return new HyphenationAuto(lang, country, 22);
    }
    // leftMin and rightMin value
    int leftMin;
    int rightMin = 2;
    value = country.substring(pos + 1);
    country = country.substring(0, pos);
    pos = value.indexOf(',');
    if (pos == -1) {
      leftMin = Integer.parseInt(value);
    else {
      leftMin = Integer.parseInt(value.substring(0, pos));
      rightMin = Integer.parseInt(value.substring(pos + 1));
    }
    return new HyphenationAuto(lang, country, leftMin, rightMin);
  }

  /**
   * Creates a LineSeparator.
   @param attrs the attributes
   @param offset
   @return a LineSeparator
   @since 5.0.6
   */
  public LineSeparator createLineSeparator(final Map<String, String> attrs, final float offset) {
    // line thickness
    float lineWidth = 1;
    String size = attrs.get(HtmlTags.SIZE);
    if (size != null) {
      float tmpSize = HtmlUtilities.parseLength(size, HtmlUtilities.DEFAULT_FONT_SIZE);
      if (tmpSize > 0)
        lineWidth = tmpSize;
    }
    // width percentage
    String width = attrs.get(HtmlTags.WIDTH);
    float percentage = 100;
    if (width != null) {
      float tmpWidth = HtmlUtilities.parseLength(width, HtmlUtilities.DEFAULT_FONT_SIZE);
      if (tmpWidth > 0percentage = tmpWidth;
      if (!width.endsWith("%"))
        percentage = 100// Treat a pixel width as 100% for now.
    }
    // line color
    BaseColor lineColor = null;
    // alignment
    int align = HtmlUtilities.alignmentValue(attrs.get(HtmlTags.ALIGN));
    return new LineSeparator(lineWidth, percentage, lineColor, align, offset);
  }

  /**
   @param src
   @param attrs
   @param chain
   @param document
   @param img_provider
   @param img_store
   @param img_baseurl
   @return the Image
   @throws DocumentException
   @throws IOException
   */
  public Image createImage(
      String src,
      final Map<String, String> attrs,
      final ChainedProperties chain,
      final DocListener document,
      final ImageProvider img_provider,
      final HashMap<String, Image> img_store,
      final String img_baseurlthrows DocumentException, IOException {
    Image img = null;
    // getting the image using an image provider
    if (img_provider != null)
      img = img_provider.getImage(src, attrs, chain, document);
    // getting the image from an image store
    if (img == null && img_store != null) {
      Image tim = img_store.get(src);
      if (tim != null)
        img = Image.getInstance(tim);
    }
    if (img != null)
      return img;
    // introducing a base url
    // relative src references only
    if (!src.startsWith("http"&& img_baseurl != null) {
      src = img_baseurl + src;
    }
    else if (img == null && !src.startsWith("http")) {
      String path = chain.getProperty(HtmlTags.IMAGEPATH);
      if (path == null)
        path = "";
      src = new File(path, src).getPath();
    }
    img = Image.getInstance(src);
    if (img == null)
      return null;

    float actualFontSize = HtmlUtilities.parseLength(
      chain.getProperty(HtmlTags.SIZE),
      HtmlUtilities.DEFAULT_FONT_SIZE);
    if (actualFontSize <= 0f)
      actualFontSize = HtmlUtilities.DEFAULT_FONT_SIZE;
    String width = attrs.get(HtmlTags.WIDTH);
    float widthInPoints = HtmlUtilities.parseLength(width, actualFontSize);
    String height = attrs.get(HtmlTags.HEIGHT);
    float heightInPoints = HtmlUtilities.parseLength(height, actualFontSize);
    if (widthInPoints > && heightInPoints > 0) {
      img.scaleAbsolute(widthInPoints, heightInPoints);
    else if (widthInPoints > 0) {
      heightInPoints = img.getHeight() * widthInPoints
          / img.getWidth();
      img.scaleAbsolute(widthInPoints, heightInPoints);
    else if (heightInPoints > 0) {
      widthInPoints = img.getWidth() * heightInPoints
          / img.getHeight();
      img.scaleAbsolute(widthInPoints, heightInPoints);
    }

    String before = chain.getProperty(HtmlTags.BEFORE);
    if (before != null)
      img.setSpacingBefore(Float.parseFloat(before));
    String after = chain.getProperty(HtmlTags.AFTER);
    if (after != null)
      img.setSpacingAfter(Float.parseFloat(after));
    img.setWidthPercentage(0);
    return img;
  }

  /**
   @param tag
   @param chain
   @return the List
   */
  public List createList(final String tag, final ChainedProperties chain) {
    List list;
    if (HtmlTags.UL.equalsIgnoreCase(tag)) {
      list = new List(List.UNORDERED);
      list.setListSymbol("\u2022 ");
    }
    else {
      list = new List(List.ORDERED);
    }
    try{
      list.setIndentationLeft(new Float(chain.getProperty(HtmlTags.INDENT)).floatValue());
    }catch (Exception e) {
      list.setAutoindent(true);
    }
    return list;
  }
}