Perfect! Minor changes - use SVG12BridgeContext, and catch
DomException when checking the width and height of the
SVGTextPositioningElement (getExtentOfChar does not return null even
if the last character has overflowed).
In the interest of making others' lives easier (pay it forward),
here's the complete solution to not only detect overflow in a flowText
region, but also resize the font-size automatically to maximize the
size of the displayed text.
The code:
---
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.bridge.svg12.SVG12BridgeContext;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.dom.svg.SVGOMElement;
import org.apache.batik.dom.svg.SVGTextContent;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.DOMException;
import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGRect;
import org.w3c.dom.svg.SVGTextPositioningElement;
public class Test
{
public static void main(String[] args)
{
try
{
// Open the SVG template.
String parser =
XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory f = new
SAXSVGDocumentFactory(parser);
SVGDocument doc = f.createSVGDocument(new
File("test.svg").toURI().toString());
Element testElement = doc.getElementById("test");
UserAgent userAgent = new UserAgentAdapter();
DocumentLoader loader = new DocumentLoader(userAgent);
SVG12BridgeContext ctx = new
SVG12BridgeContext(userAgent, loader);
GVTBuilder builder;
GraphicsNode rootGN;
// Boot the CSS and DOM interfaces.
ctx.setDynamicState(SVG12BridgeContext.DYNAMIC);
builder = new GVTBuilder();
rootGN = builder.build(ctx, doc);
while (overflows(testElement))
{
int fontSize= Integer.parseInt(testElement.getAttribute("font-
size"));
testElement.setAttribute("font-size", (new Integer(fontSize *=
0.5)).toString());
}
int pointSize1 = Integer.parseInt(testElement.getAttribute("font-
size"));
while (!overflows(testElement))
{
int fontSize= Integer.parseInt(testElement.getAttribute("font-
size"));
testElement.setAttribute("font-size", (new Integer(fontSize *=
2)).toString());
}
int pointSize2 = Integer.parseInt(testElement.getAttribute("font-
size"));
int pointSize = pointSize2;
float divider = pointSize2 - pointSize1;
while (true)
{
if (divider < 0.01) { break; }
if (!overflows(testElement))
{
pointSize = pointSize += divider;
testElement.setAttribute("font-size", (new
Integer(pointSize)).toString());
divider = divider/2;
}
else
{
pointSize = pointSize -= divider;
testElement.setAttribute("font-size", (new
Integer(pointSize)).toString());
divider = divider/2;
}
}
while (overflows(testElement))
{
pointSize =
Integer.parseInt(testElement.getAttribute("font-size"));
pointSize -= divider;
testElement.setAttribute("font-size", (new
Integer(pointSize)).toString());
}
// Render the rasterized output file.
JPEGTranscoder t = new JPEGTranscoder();
// Set the transcoding hints.
t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(.
8));
// Create the transcoder input.
TranscoderInput input = new TranscoderInput(doc);
// Create the transcoder output.
OutputStream ostream = new FileOutputStream("output.jpg");
TranscoderOutput output = new TranscoderOutput(ostream);
// Save the image.
t.transcode(input, output);
// Flush and close the stream.
ostream.flush();
ostream.close();
}
catch (IOException ex)
{
System.out.println("Unable to read the template SVG
file!");
}
catch (TranscoderException te)
{
System.out.println("Unable to render the template SVG file to an
image!");
System.exit(1);
}
}
private static boolean overflows(Element textElement)
{
SVGTextPositioningElement positioning =
(SVGTextPositioningElement) textElement;
int numberOfChars = ((SVGTextContent)
((SVGOMElement)textElement).getSVGContext()).getNumberOfChars();
SVGRect textBounds = positioning.getExtentOfChar(numberOfChars -
1);
try
{
textBounds.getWidth();
textBounds.getHeight();
return false;
}
catch (DOMException e)
{
return true;
}
}
}
---
The test svg:
---
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
width="500px" height="500px" viewBox="0 0 500 500">
<flowText xml:space="preserve" xmlns="http://xml.apache.org/batik/
ext" overflow="hidden">
<flowRegion vertical-align="middle">
<rect x="0" y="0" width="500" height="500"/>
</flowRegion>
<flowDiv>
<flowPara><flowLine id="test" font-size="72">This is a test
paragraph.</flowLine></flowPara>
</flowDiv>
</flowText>
</svg>
---
The result will be a 500x500px area that has the text maximized in the
flowText region.
Brendon
On Dec 15, 2009, at 2:43 AM, [email protected] wrote:
Hi Brendon,
"Brendon J. Wilson" <[email protected]> wrote on 12/14/2009
09:16:07 PM:
> Interesting, so I've taken your advice but it appears the
> SVGTextPositioningElement.getExtentOfChar method throws a
> NullPointerException [...].
> Is there some other initialization I need to do before
> I attempt to use this method?
Yes you must boot the SVG & CSS DOM before you
try and use them:
http://wiki.apache.org/xmlgraphics-batik/BootSvgAndCssDom
> // Open the SVG template.
> String parser =
XMLResourceDescriptor.getXMLParserClassName();
> SAXSVGDocumentFactory f = new
SAXSVGDocumentFactory(parser);
> SVGDocument doc = f.createSVGDocument(new
File("test.svg").toURI().toString());
Insert the Initialization code here...
> Element testElement = doc.getElementById("test");
> SVGTextPositioningElement positioning =
(SVGTextPositioningElement)
> testElement;
> SVGRect testBounds = positioning.getExtentOfChar(3);
>
>
> if (testBounds != null)
> {
> System.out.println("No overflow!");
> }
> else
> {
> System.out.println("Overflow!");
> }
> }
> catch (IOException ex)
> {
> System.out.println("Unable to read the template SVG
file!");
> }
> }
> }
>
> ---
>
> Thanks in advance,
>
> Brendon
>
>
> On Dec 14, 2009, at 5:30 AM, [email protected] wrote:
>
> > Hi Brendon,
> >
> > "Brendon J. Wilson" <[email protected]> wrote on 12/13/2009
> > 11:24:25 PM:
> >
> > > The Desired Result: An area of text that uses the largest font
size
> > > possible while still fitting into a given rectangle.
> > >
> > > The Proposed Solution: Use the 'flowroot' element, check for
> > overflow,
> > > and iteratively adjust the font size of the text.
> > >
> > > The Problem: Based on emails from this list dating from 2006, it
> > does
> > > not appear overflow events are implemented, and my alternative
> > > attempts to formulate my own overflow detection mechanism have
been
> > > unsuccessful.
> >
> > I haven't checked the recently but my recollection is that
there
> > were some issues with the way those events were specified that
made
> > it problematic to implement them.
> >
> >
> > > but a similar call to attempt to get the bounding box for the
laid
> > out
> > > text (I presume, I'm actually shooting in the dark at this
point)
> > > results in an uncaught exception:
> > >
> > > ---
> > > SVGElement svgElement = (SVGElement)
doc.getElementById("title");
> > > SVGLocatable locatable = (SVGLocatable) svgElement;
> > > SVGRect altbounds = locatable.getBBox();
> >
> > The problem is that the flowDiv/flowPara/flowSpan implement the
> > SVG Text positioning interface rather than SVG Locatable. You
should
> > be able to detect overflow by checking if the 'getExtentOfChar'
for
> > the last 'printing char' is empty (i.e. the flow text hid it).
> >
> > > Is there another, easier way to figure out the actual
dimensions of
> > > the laid out text in a flowDiv, or check the flowRoot for
> > overflow? Or
> > > are overflow events still not implemented?
> >
> > The layout code actually tracks if overflow occurs but it
isn't
> > exposed anywhere. In 'FlowTextPainter.getTextRuns' the 'textWrap'
> > function called at the end returns true if there was overflow, but
> > that return value is propagated anywhere.
> >
>
> Thanks,
>
> Brendon
> ---
> Brendon J. Wilson
> www.brendonwilson.com
>
>
>
---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
Thanks,
Brendon
---
Brendon J. Wilson
www.brendonwilson.com
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]