1
votes

I'm looking for a way to create a jasper report element that represents a dynamic height and label text box as a textField that stretch with overflow

The content of the text box should include a dotted line for each line from beginning to end.

Rough sketch of desired result.

A rough sketch of the final result.

Final Decision

public class JRSplitText {
    private String text;
    private int firstLineCharacterCount = 0;

    private AttributedString attributedString;

    public JRSplitText(String text, String fontName, int fontStyle, float fontSize) {
        this.text = text;

        FontUtil fontUtil = FontUtil.getInstance(DefaultJasperReportsContext.getInstance());
        Font font = fontUtil.getAwtFontFromBundles(fontName, fontStyle, fontSize, Locale.getDefault(), true);

        attributedString = new AttributedString(text);
        attributedString.addAttribute(TextAttribute.FONT, font);
    }

    public String getText(int wrappingWidth) {
        if(text == null) {
            return null;
        }

        AffineTransform transform = new AffineTransform();
        FontRenderContext frc = new FontRenderContext(transform, true, true);

        AttributedCharacterIterator iterator = attributedString.getIterator();
        LineBreakMeasurer measurer = new LineBreakMeasurer(iterator, frc);

        TextLayout layout = measurer.nextLayout(wrappingWidth);
        firstLineCharacterCount = layout.getCharacterCount();

        return text.substring(0, firstLineCharacterCount);
    }

    public JRDataSource getDataSource(int wrappingWidth) {
        if(text == null) {
            return null;
        }

        List<String> list = new ArrayList<>();

        AffineTransform transform = new AffineTransform();
        FontRenderContext frc = new FontRenderContext(transform, true, true);

        AttributedCharacterIterator iterator = attributedString.getIterator();
        LineBreakMeasurer measurer = new LineBreakMeasurer(iterator, frc);
        measurer.setPosition(firstLineCharacterCount);

        while (measurer.getPosition() < iterator.getEndIndex()) {
            TextLayout layout = measurer.nextLayout(wrappingWidth);
            String line = text.substring(measurer.getPosition() - layout.getCharacterCount(), measurer.getPosition());

            list.add(line);
        }

        if(list.isEmpty()) {
            return null;
        }

        return new JRBeanCollectionDataSource(list);
    }
}

and test jrxml file

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.4.0.final using JasperReports Library version 6.4.1  -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Blank_A4_1" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="f9a6be97-9be4-4067-9185-a2a909bd704c">
    <property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
    <subDataset name="Dataset1" uuid="ebf3dce1-fb7a-427e-b156-4053fbbdd833">
        <queryString>
            <![CDATA[]]>
        </queryString>
        <field name="_THIS" class="java.lang.String"/>
    </subDataset>
    <parameter name="lorem ipsum" class="java.lang.String"/>
    <parameter name="testParam" class="JRSplitText" isForPrompting="false">
        <parameterDescription><![CDATA[]]></parameterDescription>
        <defaultValueExpression><![CDATA[new JRSplitText($P{lorem ipsum}, "SansSerif", java.awt.Font.BOLD, 10.0f)]]></defaultValueExpression>
    </parameter>
    <queryString>
        <![CDATA[]]>
    </queryString>
    <background>
        <band splitType="Stretch"/>
    </background>
    <columnHeader>
        <band height="16" splitType="Stretch">
            <frame>
                <reportElement x="0" y="0" width="556" height="0" uuid="422c3774-9917-4908-af5d-d695eb4c0af0">
                    <property name="com.jaspersoft.studio.unit.height" value="pixel"/>
                </reportElement>
                <box>
                    <topPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                    <leftPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                    <bottomPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                    <rightPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                </box>
                <staticText>
                    <reportElement x="0" y="0" width="100" height="18" uuid="258f51ab-e5f3-4c84-a483-87d03ff4adf7"/>
                    <textElement verticalAlignment="Bottom"/>
                    <text><![CDATA[This is a Label:]]></text>
                </staticText>
                <textField>
                    <reportElement x="100" y="0" width="456" height="18" uuid="aadc75c9-2599-4051-a37d-af3583ec3635"/>
                    <box>
                        <bottomPen lineWidth="1.0" lineStyle="Dotted"/>
                    </box>
                    <textElement verticalAlignment="Bottom">
                        <font fontName="SansSerif" size="10" isBold="true"/>
                        <paragraph leftIndent="2"/>
                    </textElement>
                    <textFieldExpression><![CDATA[$P{testParam}.getText(454)]]></textFieldExpression>
                </textField>
                <staticText>
                    <reportElement x="100" y="18" width="456" height="8" uuid="059e4872-c8f0-44f7-ac32-4e7b42ce1942">
                        <property name="com.jaspersoft.studio.unit.height" value="pixel"/>
                    </reportElement>
                    <textElement textAlignment="Center">
                        <font size="6"/>
                    </textElement>
                    <text><![CDATA[(hint ...)]]></text>
                </staticText>
                <componentElement>
                    <reportElement x="0" y="18" width="556" height="18" isRemoveLineWhenBlank="true" uuid="aaa64777-42be-45ff-8045-9916e75aaf31"/>
                    <jr:list xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd" printOrder="Vertical">
                        <datasetRun subDataset="Dataset1" uuid="5f0160cd-5fef-43e9-970e-fde5226a2aeb">
                            <dataSourceExpression><![CDATA[$P{testParam}.getDataSource(556)]]></dataSourceExpression>
                        </datasetRun>
                        <jr:listContents height="18" width="556">
                            <textField isBlankWhenNull="false">
                                <reportElement x="0" y="0" width="556" height="18" isRemoveLineWhenBlank="true" uuid="f0ba1742-9bae-452c-9e98-3518e0951571">
                                    <property name="com.jaspersoft.studio.unit.height" value="pixel"/>
                                </reportElement>
                                <box>
                                    <bottomPen lineWidth="1.0" lineStyle="Dotted"/>
                                </box>
                                <textElement verticalAlignment="Bottom">
                                    <font isBold="true"/>
                                    <paragraph leftIndent="0"/>
                                </textElement>
                                <textFieldExpression><![CDATA[$F{_THIS}]]></textFieldExpression>
                            </textField>
                        </jr:listContents>
                    </jr:list>
                </componentElement>
            </frame>
            <staticText>
                <reportElement positionType="Float" x="455" y="0" width="100" height="16" forecolor="#857D7D" uuid="8bee13b8-2322-489b-8ab0-2e3c7c6742d1">
                    <property name="com.jaspersoft.studio.unit.y" value="pixel"/>
                </reportElement>
                <textElement textAlignment="Right"/>
                <text><![CDATA[Static Text]]></text>
            </staticText>
        </band>
    </columnHeader>
</jasperReport>

enter image description here

1
"without emphasizing the label" what do you mean with this statement?Petter Friberg
AFIK there is no simple way to do what you need. Method 1) Calculate where lines splits and convert to datasource. Method 2) Write a customize render. Do you have possibility to elaborate your string in java before you pass it to jasper-report?Petter Friberg
Yes, I have the opportunity to do whatever it takes. But as I understand your idea, I will need access to the context in which the strings will be drawn. Otherwise, I do not see how I can determine where to break the string. I will check in the documentation what options I have to create a custom render, but I would enjoy some direction.mr mcwolf

1 Answers

2
votes

There are no settings in JasperReports that let you achieve this directly, one of the simplest strategy probably is to convert your text to a datasource, hence a list of lines, then you display every line in a textField with a dotted line under it.

To create the datasource we need to calculate when the line break will occur and for this we need to use java, I will use the LineBreakMeasurer

Java code converting a long text into a list of lines, depending on wrappingWidth and Font

public class JRSplitText {

    public static JRDataSource getTextDataSource(String text, int wrappingWidth, String fontName, int fontStyle, int fontSize) {
        // Our list with the different text segements (lines)
        List<String> textSegements = new ArrayList<>();

        // Instance a FontRenderContext
        AffineTransform affinetransform = new AffineTransform();
        FontRenderContext frc = new FontRenderContext(affinetransform, true, true);

        // Instance the AttributedString and it's iterator
        AttributedString attrText = new AttributedString(text);
        // To consider font-extensions lets load font directly from jasper-reports
        FontUtil fontUtil = FontUtil.getInstance(DefaultJasperReportsContext.getInstance());
        Font font = fontUtil.getAwtFontFromBundles(fontName, fontStyle, fontSize, Locale.getDefault(), false);
        attrText.addAttribute(TextAttribute.FONT, font);
        AttributedCharacterIterator styledText = attrText.getIterator();

        // Start measurer how much text can fit in our width and add each line
        // to our list
        LineBreakMeasurer measurer = new LineBreakMeasurer(styledText, frc);
        while (measurer.getPosition() < styledText.getEndIndex()) {
            TextLayout layout = measurer.nextLayout(wrappingWidth);
            String line = text.substring(measurer.getPosition() - layout.getCharacterCount(), measurer.getPosition());
            textSegements.add(line);
        }

        // return a datasource of our lists
        return new JRBeanCollectionDataSource(textSegements);
    }
}

With this code in classpath we can call it directly from jrxml with our text and font to get a datasource.

JasperReports - jrxml, we will call the class above with some text in a parameter (it could be field or variabile), in example I'm using jr:list component to iterate datasource but you can also use a normal subreport.

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="lineBreakTest" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="597c0716-df6b-42ec-a7c8-863eb1b7174a">
    <property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
    <subDataset name="datasource" uuid="6a442aa1-3bf1-494c-9654-e6d3122dd5a7">
        <queryString>
            <![CDATA[]]>
        </queryString>
        <field name="_THIS" class="java.lang.String"/>
    </subDataset>
    <parameter name="text" class="java.lang.String">
        <defaultValueExpression><![CDATA["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."]]></defaultValueExpression>
    </parameter>
    <title>
        <band height="18">
            <componentElement>
                <reportElement positionType="Float" x="2" y="0" width="550" height="15" uuid="3a68753d-ac77-4941-9dd9-fdeca5ad2f76"/>
                <jr:list xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd" printOrder="Vertical">
                    <datasetRun subDataset="datasource" uuid="a76bcffd-57ad-44bf-b2c9-20b89089e440">
                        <dataSourceExpression><![CDATA[jdd.so.JRSplitText.getTextDataSource($P{text},550,"SansSerif",java.awt.Font.PLAIN,10)]]></dataSourceExpression>
                    </datasetRun>
                    <jr:listContents height="15" width="550">
                        <textField>
                            <reportElement x="0" y="0" width="550" height="15" uuid="c9685fa5-a942-4709-9fa9-98e9cc80bc5c"/>
                            <box>
                                <bottomPen lineWidth="1.0" lineStyle="Dotted"/>
                            </box>
                            <textElement>
                                <font fontName="SansSerif" size="10"/>
                            </textElement>
                            <textFieldExpression><![CDATA[$F{_THIS}]]></textFieldExpression>
                        </textField>
                    </jr:listContents>
                </jr:list>
            </componentElement>
        </band>
    </title>
</jasperReport>

Output

result

The dotted lines does not seem nice in screen-shot, but this is only a screen shot-issue

Note: wrappingWidth and font parameters, is our width of textField and its font. The line height is controlled by height of textField