Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright (C) 2011-2015 The XDocReport Team
*
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package fr.opensagres.odfdom.converter.pdf.internal.stylable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import com.lowagie.text.Chunk;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.BaseFont;
import fr.opensagres.odfdom.converter.core.utils.ODFUtils;
import fr.opensagres.odfdom.converter.pdf.internal.styles.Style;
import fr.opensagres.odfdom.converter.pdf.internal.styles.StyleBreak;
import fr.opensagres.odfdom.converter.pdf.internal.styles.StyleLineHeight;
import fr.opensagres.odfdom.converter.pdf.internal.styles.StyleParagraphProperties;
import fr.opensagres.odfdom.converter.pdf.internal.styles.StyleTextProperties;
import fr.opensagres.xdocreport.itext.extension.ExtendedParagraph;
/**
* fixes for paragraph pdf conversion by Leszek Piotrowicz
*/
public class StylableParagraph
extends ExtendedParagraph
implements IStylableContainer
{
private static final long serialVersionUID = 664309269352903329L;
public static final float DEFAULT_LINE_HEIGHT = 1.0f;
private final StylableDocument ownerDocument;
private IStylableContainer parent;
private Style lastStyleApplied = null;
private boolean elementPostProcessed = false;
public StylableParagraph( StylableDocument ownerDocument, IStylableContainer parent )
{
super();
this.ownerDocument = ownerDocument;
this.parent = parent;
super.setMultipliedLeading( DEFAULT_LINE_HEIGHT );
}
public StylableParagraph( StylableDocument ownerDocument, Paragraph title, IStylableContainer parent )
{
super( title );
this.ownerDocument = ownerDocument;
this.parent = parent;
super.setMultipliedLeading( DEFAULT_LINE_HEIGHT );
}
public void applyStyles( Style style )
{
this.lastStyleApplied = style;
StyleTextProperties textProperties = style.getTextProperties();
if ( textProperties != null )
{
// font
Font font = textProperties.getFont();
if ( font != null )
{
super.setFont( font );
}
}
StyleParagraphProperties paragraphProperties = style.getParagraphProperties();
if ( paragraphProperties != null )
{
// break-before
StyleBreak breakBefore = paragraphProperties.getBreakBefore();
if ( breakBefore != null )
{
handleBreak( breakBefore );
}
// alignment
int alignment = paragraphProperties.getAlignment();
if ( alignment != Element.ALIGN_UNDEFINED )
{
super.setAlignment( alignment );
}
// first line indentation
Boolean autoTextIndent = paragraphProperties.getAutoTextIndent();
if ( Boolean.TRUE.equals( autoTextIndent ) )
{
float fontSize = font != null ? font.getCalculatedSize() : Font.DEFAULTSIZE;
super.setFirstLineIndent( 1.3f * fontSize );
}
else
{
Float textIndent = paragraphProperties.getTextIndent();
if ( textIndent != null)
{
// text indent can be negative.
// See https://code.google.com/p/xdocreport/issues/detail?id=366
super.setFirstLineIndent( textIndent );
}
}
// line height
StyleLineHeight lineHeight = paragraphProperties.getLineHeight();
if ( lineHeight != null && lineHeight.getLineHeight() != null )
{
if ( lineHeight.isLineHeightProportional() )
{
super.setMultipliedLeading( lineHeight.getLineHeight() );
}
else
{
super.setLeading( lineHeight.getLineHeight() );
}
}
// keep together on the same page
Boolean keepTogether = paragraphProperties.getKeepTogether();
if ( keepTogether != null )
{
super.setKeepTogether( keepTogether );
}
}
}
private void handleBreak( StyleBreak styleBreak )
{
if ( styleBreak.isColumnBreak() || styleBreak.isPageBreak() )
{
IBreakHandlingContainer b = StylableDocumentSection.getIBreakHandlingContainer( parent );
if ( b != null )
{
if ( styleBreak.isColumnBreak() )
{
b.columnBreak();
}
else if ( styleBreak.isPageBreak() )
{
b.pageBreak();
}
}
}
}
public Style getLastStyleApplied()
{
return lastStyleApplied;
}
public IStylableContainer getParent()
{
return parent;
}
public StylableDocument getOwnerDocument()
{
return ownerDocument;
}
public static Chunk createAdjustedChunk( String content, Font font, float lineHeight, boolean lineHeightProportional )
{
// adjust chunk attributes like text rise
// use StylableParagraph mechanism
StylableParagraph p = new StylableParagraph( null, null );
p.setFont( font );
if ( lineHeightProportional )
{
p.setMultipliedLeading( lineHeight );
}
else
{
p.setLeading( lineHeight );
}
p.addElement( new Chunk( content, font ) );
p.getElement(); // post-processing here
return (Chunk) p.getChunks().get( 0 );
}
@Override
public Element getElement()
{
if ( !elementPostProcessed )
{
elementPostProcessed = true;
postProcessEmptyParagraph();
postProcessBookmarks();
postProcessLineHeightAndBaseline();
}
return super.getElement();
}
@SuppressWarnings( "unchecked" )
private void postProcessEmptyParagraph()
{
// add space if this paragraph is empty
// otherwise its height will be zero
boolean empty = true;
ArrayList chunks = getChunks();
for ( Chunk chunk : chunks )
{
if ( chunk.getImage() == null && chunk.getContent() != null && chunk.getContent().length() > 0 )
{
empty = false;
break;
}
}
if ( empty )
{
super.add( new Chunk( ODFUtils.TAB_STR ) );
}
}
@SuppressWarnings( "unchecked" )
private void postProcessBookmarks()
{
// add space if last chunk is a bookmark
// otherwise the bookmark will disappear from pdf
ArrayList chunks = getChunks();
if ( chunks.size() > 0 )
{
Chunk lastChunk = chunks.get( chunks.size() - 1 );
String localDestination = null;
if ( lastChunk.getAttributes() != null )
{
localDestination = (String) lastChunk.getAttributes().get( Chunk.LOCALDESTINATION );
}
if ( localDestination != null )
{
super.add( new Chunk( ODFUtils.NON_BREAKING_SPACE_STR ) );
}
}
}
@SuppressWarnings( "unchecked" )
private void postProcessLineHeightAndBaseline()
{
// adjust line height and baseline
Font font = getMostOftenUsedFont();
if ( font == null || font.getBaseFont() == null )
{
font = this.font;
}
if ( font != null && font.getBaseFont() != null )
{
// iText and open office computes proportional line height differently
// [iText] line height = coefficient * font size
// [open office] line height = coefficient * (font ascender + font descender + font extra margin)
// we have to increase paragraph line height to generate pdf similar to open office document
// this algorithm may be inaccurate if fonts with different multipliers are used in this paragraph
float size = font.getSize();
float ascender = font.getBaseFont().getFontDescriptor( BaseFont.AWT_ASCENT, size );
float descender = -font.getBaseFont().getFontDescriptor( BaseFont.AWT_DESCENT, size ); // negative value
float margin = font.getBaseFont().getFontDescriptor( BaseFont.AWT_LEADING, size );
float multiplier = ( ascender + descender + margin ) / size;
if ( multipliedLeading > 0.0f )
{
setMultipliedLeading( getMultipliedLeading() * multiplier );
}
// iText seems to output text with baseline lower than open office
// we raise all paragraph text by some amount
// again this may be inaccurate if fonts with different size are used in this paragraph
float itextdescender = -font.getBaseFont().getFontDescriptor( BaseFont.DESCENT, size ); // negative
float textRise = itextdescender + getTotalLeading() - font.getSize() * multiplier;
ArrayList chunks = getChunks();
for ( Chunk chunk : chunks )
{
Font f = chunk.getFont();
if ( f != null )
{
// have to raise underline and strikethru as well
float s = f.getSize();
if ( f.isUnderlined() )
{
f.setStyle( f.getStyle() & ~Font.UNDERLINE );
chunk.setUnderline( s * 1 / 17, s * -1 / 7 + textRise );
}
if ( f.isStrikethru() )
{
f.setStyle( f.getStyle() & ~Font.STRIKETHRU );
chunk.setUnderline( s * 1 / 17, s * 1 / 4 + textRise );
}
}
chunk.setTextRise( chunk.getTextRise() + textRise );
}
}
}
@SuppressWarnings( "unchecked" )
private Font getMostOftenUsedFont()
{
// determine font most often used in this paragraph
// font with the highest count of non-whitespace characters
// is considered to be most often used
Map fontMap = new LinkedHashMap();
Map countMap = new LinkedHashMap();
Font mostUsedFont = null;
int mostUsedCount = -1;
ArrayList chunks = getChunks();
for ( Chunk chunk : chunks )
{
Font font = chunk.getFont();
int count = 0;
String text = chunk.getContent();
if ( text != null )
{
// count non-whitespace characters in a chunk
for ( int i = 0; i < text.length(); i++ )
{
char ch = text.charAt( i );
if ( !Character.isWhitespace( ch ) )
{
count++;
}
}
}
if ( font != null )
{
// update font and its count
String fontKey = font.getFamilyname() + "_" + (int) font.getSize();
Font fontTmp = fontMap.get( fontKey );
if ( fontTmp == null )
{
fontMap.put( fontKey, font );
}
Integer countTmp = countMap.get( fontKey );
int totalCount = countTmp == null ? count : countTmp + count;
countMap.put( fontKey, totalCount );
// update most used font
if ( totalCount > mostUsedCount )
{
mostUsedCount = totalCount;
mostUsedFont = font;
}
}
}
return mostUsedFont;
}
}