On my WinXP box under JDK6, the following code:
import java.awt.Font;
import java.awt.GraphicsEnvironment;
public class TestFontFamily
{
public static void main(String[] args)
{
for( String s : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames() )
{
Font f = Font.decode(s);
if( f != null && !s.equals(f.getFamily()) )
System.out.println(String.format("%-20s %s", s, f));
}
}
}
prints the following list:
Bauhaus 93 java.awt.Font[family=Dialog,name=Bauhaus,style=plain,size=93]
Bookshelf Symbol 7 java.awt.Font[family=Dialog,name=Bookshelf Symbol,style=plain,size=7]
Britannic Bold java.awt.Font[family=Dialog,name=Britannic,style=bold,size=12]
Harlow Solid Italic java.awt.Font[family=Dialog,name=Harlow Solid,style=italic,size=12]
Modern No. 20 java.awt.Font[family=Dialog,name=Modern No.,style=plain,size=20]
Wingdings 2 java.awt.Font[family=Wingdings,name=Wingdings,style=plain,size=2]
Wingdings 3 java.awt.Font[family=Wingdings,name=Wingdings,style=plain,size=3]
So the question is why font family name from Font#decode is different? And which one should I use?
It seems both have problems: getAvailableFontFamilyNames() includes style/size, and what I get from Font#decode may have wrong family/name.
A bonus question is then: What is a reliable way to get a list of fonts?
The following code does not work either, as some fonts' "name" differs from "family name", e.g. the family name of "Berlin Sans FB Demi Bold" is "Berlin Sans FB Demi".
for( Font f : GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts() )
{
if( f.getFamily().equals(f.getName()) )
System.out.println(f);
}
(I wonder what badge I may get for answering my own tumbleweed question.)
I found that Font.decode(s + " plain") gives me fonts with the right family names.
Related
It seems that Swing auto decorate some Arabic words by making them look some kind calligraphic. One of those words is Muhammad which is spelled in Arabic as محمد.
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TestProject extends JFrame {
public static void main(String[] args) {
TestProject frame = new TestProject();
frame.setVisible(true);
}
public TestProject() {
this.setSize(200, 100);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("محمد");
label.setFont(new Font("Arial", Font.BOLD, 28));
this.add(label);
}
}
The problem is applied to all widely used Fonts at least.
I'm using Windows 10 & Java 8. I've tried the same program on another PC (also Windows 10) same problem.
How can we disable that?
This seems to be a bug in the old (i.e. up to Java 8) font rendering engine.
This is how it looks with Java 8 on my machine:
And this is the result with Java 9:
I don’t know whether an explicit bug report has been reported for this specific issue. But the font engine has been replaced entirely, due to JEP 258: HarfBuzz Font-Layout Engine.
Replace the existing ICU OpenType font-layout engine with HarfBuzz.
So it’s not surprising that bugs of the old font rendering are gone.
(Caveat: I am not literate in Arabic.)
Appears to be font related
By default, I do not get your longer representation (3 words?) as seen in your screenshot.
When I write a minimal Swing app with the text "محمد" I get that short string (4 characters?) that you want.
When using specific fonts, I do get what looks to my untrained eye to be the calligraphic rendering you seek to avoid.
Note that my string was not your string. I copied from Google Translate, translating Muhammad as indicated in your Question.
https://translate.google.com/?sl=en&tl=ar&text=Muhammad%0A&op=translate
I wrote this minimal Swing app with just a JLabel, enhanced only by increasing font size.
package work.basil.example;
import javax.swing.*;
import java.awt.*;
/**
* Hello world!
*/
public class App {
public static void main ( String[] args ) {
System.out.println( Runtime.version() );
javax.swing.SwingUtilities.invokeLater( () -> createAndShowGUI() );
}
private static void createAndShowGUI () {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated( true );
//Create and set up the window.
JFrame frame = new JFrame( "Test Arabic rendering" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
//Add the ubiquitous "Hello World" label.
JLabel label = new JLabel( "محمد" );
label.setFont( new Font( label.getFont().getName() , label.getFont().getStyle() , 40 ) ); // Override default font size.
System.out.println( "Font name: " + label.getFont().getName() + " | " + "Font style: " + label.getFont().getStyle() );
frame.getContentPane().add( label );
//Display the window.
frame.pack();
frame.setVisible( true );
}
}
When run on my MacBook Pro (13-inch, M1, 2020), Apple Silicon (not Intel), 16 gigs of memory. The OS is macOS Big Sur, 11.5.1. Using early-access version of Java 17 (17+35-2724) from this page.
(By the way, this is the kind of MCVE you should have posted with your Question.)
The result of my font query is:
Font name: Lucida Grande | Font style: 0
… but I suspect that is the default font of the JLabel widget rather than the actual font used in rendering those Arabic characters. As I vaguely recall, recent versions of macOS are rich with a variety of fonts specifically for Arabic. See: Fonts included with macOS Big Sur for three lists: fonts included, fonts available for download, and older fonts available for document support. Using the Font Book app bundled with macOS, and defining a "smart collection" where languages include Arabic, I get a list of 32 such fonts installed by default in macOS Big Sur. (The four items for Pragmata Pro do not apply — that is a commercial font which I purchased, and which I highly recommend for developers.)
I ran this modified version of the code from Answer by Andrew Thompson on the same macOS Big Sur described above.
package work.basil.text;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
public class TestProject extends JFrame {
public static void main ( String[] args ) {
TestProject frame = new TestProject();
frame.setVisible( true );
}
public TestProject () {
this.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
this.setLayout( new FlowLayout() );
String input = "محمد";
String[] fontFamilies = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
for ( String fontFamily : fontFamilies ) {
Font font = new Font( fontFamily , Font.PLAIN , 40 );
if ( font.canDisplayUpTo( input ) < 0 ) {
JLabel label = new JLabel( input );
label.setFont( font );
label.setBorder( new TitledBorder( fontFamily ) );
this.add( label );
}
}
this.pack();
}
}
Here is a screenshot of the result.
Try running the code below. It produced this image on my range of installed fonts.
Note also that Arial (a sans-serif or undecorated font) seems to default to Times New Roman (a serif or decorated font) for its Arabic glyphs. This is a common thing for fonts (or systems, not sure) to do, and it produces rather odd renderings when sans-serif fonts are replaced with serif fonts.
Also note this system has 255 fonts installed, but only 23 of those support Arabic characters.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class TestProject extends JFrame {
public static void main(String[] args) {
TestProject frame = new TestProject();
frame.setVisible(true);
}
public TestProject() {
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel p = new JPanel(new GridLayout(0, 5));
String arabic = "محمد";
String[] fontFamilies = GraphicsEnvironment.
getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
for (String fontFamily : fontFamilies) {
Font font = new Font(fontFamily, Font.PLAIN, 25);
if (font.canDisplayUpTo(arabic) < 0) {
JLabel label = new JLabel(arabic);
label.setFont(font);
label.setBorder(new TitledBorder(fontFamily));
p.add(label);
}
}
this.add(new JScrollPane(p));
this.pack();
}
}
I'm trying to fill out a bunch of PDF Forms using PDFBox 2.0.8. For some documents I get the following error when setting the PDTextField's value:
java.io.IOException: Could not find font: /ArialMT
Apparently the font is not correctly embedded as is often the case with proprietary Microsoft fonts.
How can I tell PDFBox to substitute the font e.g. with "normal" Arial or some other font? Setting the fields DA string to "/Helv 0 tf 0 g" resulted in a NullPointerException.
Based on the comments from Tilman Hausherr I built a first fix which works independent from the operating system (which is a Linux in my case).
acroForm.defaultResources.put(COSName.getPDFName("ArialMT"),
PDType0Font.load (pdDocument, this.javaClass.classLoader.getResourceAsStream("fonts/ARIALMT.ttf"), false))
This will only work for this particular font, though. What's still missing - and was actually the main intention of my question - is an option to tell PDFBox to fall back to a certain font resp. DA if the font that is required cannot be provided.
After Tilman again came for the rescue I can now present the complete solution. Again, this is Kotlin, not Java:
PDDocument.load(file).use { pdDocument ->
val acroForm = pdDocument.documentCatalog.acroForm
acroForm.defaultResources.put(COSName.getPDFName("ArialMT"),
PDType0Font.load (pdDocument, this.javaClass.classLoader.getResourceAsStream("fonts/ARIALMT.ttf"), false))
val pdField: PDField? = acroForm.getField(fieldname)
val value = ...
when (pdField) {
is PDCheckBox -> {
if (value is Boolean) {
when (value) {
true -> pdField.check()
false -> pdField.unCheck()
}
} else {
log.error("RENDER_FORM: Need Boolean for ${pdField.fullyQualifiedName} but got $value")
}
}
is PDTextField -> {
try {
pdField.value = value?.toString() ?: ""
} catch (ioException: IOException) {
pdField.cosObject.setString(COSName.DA, "/Helv 0 Tf 0 g")
pdField.value = value?.toString() ?: ""
log.error("RENDER_FORM: Writing text field failed: ${ioException.message}")
}
}
null -> {
log.error("RENDER_FORMULAR: Formfield $fieldname does not exist in $name")
}
else -> log.error("RENDER_FORMULAR: Formfield $pdField ($fieldname) is of unhandled type ${pdField.fieldType}")
}
val stream = ByteArrayOutputStream()
pdDocument.save(stream)
pdDocument.close()
return stream.toByteArray()
}
Add "ArialMT" to the default resources:
try (PDDocument doc = PDDocument.load(new File("F2_Datenblatt_022015.pdf")))
{
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
PDField field = acroForm.getField("Vorname_Name");
// fails with IOException as described in question
//field.setValue("Tilman Hausherr");
// Method 1, just add type1 Helvetica (allows only WinAnsiEncoding glyphs)
//acroForm.getDefaultResources().put(COSName.getPDFName("ArialMT"), PDType1Font.HELVETICA);
// Method 2, add the full Arial font (allows for more different glyphs)
// important: use the method that switches off subsetting
acroForm.getDefaultResources().put(
COSName.getPDFName("ArialMT"),
PDType0Font.load(doc, new FileInputStream("c:/windows/fonts/arial.ttf"), false));
field.setValue("Tilman Hausherr");
doc.save("F2_Datenblatt_022015-mod.pdf");
}
Update:
Turns out the code in the question would have worked too with the file - almost. It's "Tf" and not "tf", so the string would have been "/Helv 0 Tf 0 g". We'll research how to avoid an NPE and get a meaningful exception.
Background
I am new to both GraphStream and Java. However, I do have experience with other OOP-languages like C++. I personally find the tutorials for GraphStream quite sparse, for example the Layout API doesn't mention which layout algorithms are valid. Thus am looking for a more consolidated tutorial.
Question
Using GraphStream, I want to be able to do the following basic operations:
Import a graph as a .dgs
Import a graph as a .csv
Change from the automatic layout to a specified layout (please list which are valid in the first place too)
Change all edge attributes (e.g. weight)
Find at most n paths of length l or less from s to t.
Add an event (e.g. click node, get user input about about length of paths from that node to show, prune/grow appropriately)
Code
Note
I am using Eclipse. From the GraphStream repository, I have added the following to my build path: gs-algo-1.3, gs-core-1.3, gs-openord-master, and gs-ui-1.3
//import anything I might need
import org.graphstream.graph.*;
import org.graphstream.graph.implementations.*;
import org.graphstream.stream.file.*;
import org.graphstream.algorithm.*;
import org.graphstream.ui.layout.*;
import java.io.IOException;
#SuppressWarnings("unused")
public class pleaseHelpMe {
public static void main(String args[]) throws IOException, InterruptedException {
//do something that was in some code somewhere...
//please explain
System.setProperty("org.graphstream.ui.renderer","org.graphstream.ui.j2dviewer.J2DGraphRenderer");
//instantiate a graph
Graph graph = new SingleGraph("thisGraphNeedsHelp");
graph.addAttribute("ui.antialias"); //this does something...
graph.addAttribute("ui.quality"); //this sounds less important...
graph.setAttribute("ui.stylesheet", "url(../myStyle.css);"); //get some style
//errors, errors everywhere
//OpenOrdLayout layout = new OpenOrdLayout();
//layout.init(graph);
//layout.compute();
//changing all edges layout.weight is not that simple
//graph.setAttribute("edges","layout.weight:4");
graph.dsiplay();//this just makes the canvas?
FileSource source = new FileSourceDGS(); //prep. to get a file
//make graph a sink of source to get the contents of the file
source.addSink(graph);
source.begin("../dgs_files/awesomeFile.dgs");//read the file? strange command name i.m.o. file below.
while(source.nextEvents()); //keep graph interactive? or is this the GraphStream version of readline?
source.end() //close file? or end interaction?
//find paths
//get user input
}
}
DGS File
DGS004
null 0 0
an a ui.label:a
an b ui.label:b
an c ui.label:c
an d ui.label:d
an e ui.label:e
an f ui.label:f
ae a_c a > c
ae c_e c > e
ae e_a e > a
ae b_d b > d
ae d_f d > f
ae f_b f > b
ae f_a f > a
ae e_b e > b
ae d_c d > c
CSS File
node:clicked {
fill-color: purple;
text-size: 16;
text-style: bold;
text-color: #FFF;
text-alignment: at-right;
text-padding: 3px, 2px;
text-background-mode: rounded-box;
text-background-color: #A7CC;
text-color: white;
text-offset: 5px, 0px;
}
node {
size: 20px;
shape: circle;
fill-color: #8facb4;
stroke-mode: plain;
stroke-color: black;
}
edge {
size: 2px;
fill-mode: plain;
/*changing edge layout here also doesn't work*/
}
/*edge:clicked isn't a thing... :( */
I need to create an User guide, where I've to put the content in 2 different language but on the same page. so the first half of the page would be in English while the second part would be in French. (In future they might ask for 3rd language also, but maximum 3). So each page would have 2 blocks. How can I achieve this using iTextPDF in java ?
UPDATE
Following is the structure for more insight of the question.
If I understand your question correctly, you need to create something like this:
In this screen shot, you see the first part of the first book of Caesar's Commentaries on the Gallic War. Gallia omnia est divisa in partes tres, and so is each page in this document: the upper part shows the text in Latin, the middle part shows the text in English, the lower part shows the text in French. If you read the text, you'll discover that Belgians like me are considered being the bravest of all (although we aren't as civilized as one would wish). See three_parts.pdf if you want to take a look at the PDF.
This PDF was created with the ThreeParts example. In this example, I have 9 text files:
http://itextpdf.com/sites/default/files/liber1_1_la.txt
http://itextpdf.com/sites/default/files/liber1_1_en.txt
http://itextpdf.com/sites/default/files/liber1_1_fr.txt
http://itextpdf.com/sites/default/files/liber1_2_la.txt
http://itextpdf.com/sites/default/files/liber1_2_en.txt
http://itextpdf.com/sites/default/files/liber1_2_fr.txt
http://itextpdf.com/sites/default/files/liber1_3_la.txt
http://itextpdf.com/sites/default/files/liber1_3_en.txt
http://itextpdf.com/sites/default/files/liber1_3_fr.txt
Liber is the latin word for book, so all files are snippets from the first book, more specifically sections 1, 2, and 3, in Latin, English and French.
This is how I defined the languages and he rectangles for each language:
public static final String[] LANGUAGES = { "la", "en", "fr" };
public static final Rectangle[] RECTANGLES = {
new Rectangle(36, 581, 559, 806),
new Rectangle(36, 308.5f, 559, 533.5f),
new Rectangle(36, 36, 559, 261) };
In my code, I loop over the different sections, and I create a ColumnText object for each language:
PdfContentByte cb = writer.getDirectContent();
ColumnText[] columns = new ColumnText[3];
for (int section = 1; section <= 3; section++) {
for (int la = 0; la < 3; la++) {
columns[la] = createColumn(cb, section, LANGUAGES[la], RECTANGLES[la]);
}
while (addColumns(columns)) {
document.newPage();
for (int la = 0; la < 3; la++) {
columns[la].setSimpleColumn(RECTANGLES[la]);
}
}
document.newPage();
}
If you examine the body of the inner loop, you see that I first define three ColumnText objects, one for each language:
public ColumnText createColumn(PdfContentByte cb, int i, String la, Rectangle rect)
throws IOException {
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(rect);
Phrase p = createPhrase(String.format("resources/text/liber1_%s_%s.txt", i, la));
ct.addText(p);
return ct;
}
In this case, I'm using ColumnText in text mode, and I read the text from the different files into a Phrase like this:
public Phrase createPhrase(String path) throws IOException {
Phrase p = new Phrase();
BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream(path), "UTF8"));
String str;
while ((str = in.readLine()) != null) {
p.add(str);
}
in.close();
return p;
}
Once I have defined the ColumnText objects and added their content, I need to render the content to one of more pages until all the text is rendered from all columns. To achieve this, we use this method:
public boolean addColumns(ColumnText[] columns) throws DocumentException {
int status = ColumnText.NO_MORE_TEXT;
for (ColumnText column : columns) {
if (ColumnText.hasMoreText(column.go()))
status = ColumnText.NO_MORE_COLUMN;
}
return ColumnText.hasMoreText(status);
}
As you can see, I also create a new page for every new section I start. This isn't really necessary: I could add all the section to a single ColumnText, but depending on how the Latin text translated into English and French, you could end up with large discrepancies where section X of the Latin text starts on one page and the same section in English or French starts on another page. Hence my choice to start a new page, although it's not really necessary in this small proof of concept.
I'm using JTextPane in an application for wiki source editing. I've added a simple spell checking functionality to it which underlines mispelled words by changing character attributes to a different style via StyledDocument.setCharacterAttributes.
There are only these two styles used: the default and the 'mispelled' one. The text editor control does word wrapping, which is the expected behavior.
My problem is that there are cases (not always, but is reproducible with a specific wiki document) this character attribute changing somehow disables the word wrapping. More specifically, I delete three lines from the middle of the document, and the next run of the spell checker, when resets the character attributes to the default style (before rerunning the spell checking), the word wrapping functionality gets disabled and it remains that way. If I undo the deletion, the word wrapping goes back to normal.
Commenting out the single line that resets the style:
editorPane.getStyledDocument().setCharacterAttributes(0, editorPane.getStyledDocument().getLength(), defaultStyle, true);
solves the issue.
EDIT 1
I've extracted the problem to a simple test case. Sorry for the long line, that example text is important to reproduce the bug (it has been randomized):
package jtextpanebug;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import javax.swing.text.Style;
public class DemoFrame extends javax.swing.JFrame {
private final JButton btResetStyle;
private final JScrollPane scrollPane;
private final JTextPane textPane;
private final Style defaultStyle;
public DemoFrame() {
// Creating a simple form with a scrollable text pane and a button
scrollPane = new JScrollPane();
textPane = new JTextPane();
btResetStyle = new JButton();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
// The text pane's text is the scrambled version of my original test data,
// it is important because the problem depends on the pane's text
// (not every text makes it wrong)
textPane.setText("= Gernela Stuff Dogo to Wnko Obuat Oebrfe Disytung hte Iidnividal Oitpcs =\n\n== Elmyonrogit ==\n\n'''memidtaie-ngiebhro opnits''' - 2 points, \nwihhc nac eb ense ot eb mmiieadte hnigeorbs fo haec othre. \nThere si no strict | cxeat defiintoin. \nEth etipcur owebl shsow sa an example optins dna the rispa \nienbg mimedtiea iebnghsor ear ncnoetced.\n\n[[Amieg:einogrhb_pinsot.pgn]]\n\n'''enihgorb optnsi distacne''' - het avaeegr of sdntaisce \nderemitedn by het mimeidate-hieobngr tonpi ipras. \n\n'''lalw''' - a iotpntes nepgesnietrr a llwa, with toerh orwds: 2 apraelll, \nevyr sloce sraufce picsee. Heer is an xamelpe. \nIt is eualgttandri ofr eterbt zisiuaitovlan.\n\n[[Gimae:llwa.npg]]\n\n'''addtaiilon emmory reeueimtnqr of na laigorthm''' - \n(eth kepa mmeory suaeg fo teh nltpiaciapo ndirug the excteouin of eht grlaotihm) - \n(hte moeymr sueag fo hte loragitmh befoer ro ftrea eht ucxeeiont of the laogrihmt)\n\n== Het Input Pnoitset Ash to Repnrsete Ufscear Arsnoelbay Elwl ==\n\nIf tno efisciped toehrwsie yb hte cdoritnpsei of an aoglirthm, \nhetn hte eqtunrmeersi of it are heste:\n\n* Ifsrt fo all the poisentt umst reprseent urfseac, not urvec ro uvomel or nayithng eesl.\n* Awlls aym otn eb tniehnr tanh at least 3 * fo ienhbgro-tpoin-sidenact.\n* Dseeg amy ton be rhserap tnha 70 grdesee (as het agnle fo eht trmeaial) nda husdol be ta tleas 290 redeseg (ni caes fo cnvocae eedgs).\n* Onpti edintsy amy ont vayr oto humc \n** Het angre fo the coall ption desitnsei of a igsenl pisnotte nutip ushold eb sallm. Ahtt is: teh orait of het oclla oitnp idsentise oarund any 2 ipnost lsdhou eb lmitied.\n** Hte lcoal noipt deisynt ushlod otn ahencg sdduelyn (gliftyscaiinn ni a hotsr idnsteac). \n\nYreftntunaoul the largoimths cna tno yb ethmsevesl \nhcekc these qutenmeserir nda usjt yden rnuning, \nso it si eth rseu's iyponerissbtil to ton extucee an raltghomi no a itseopnt \nthat does ont mete het aogitmlhr's terieseurmnq.\n\nIf eth rmeteriuqen fo na airlgmoth on its npuit is ont mte, then tobh hte ueavbhior nad hte srluet fo hte alghoritms si dinfeuned. \nTeh loirgamth amy nru rfo iinfntie long imet or rodpuce evry abd rselut, ro a eruslt htat oolsk good btu is nicrtroec. Ni htis scea rtehe si tno nay aguntreee toabu the tmniatreion of the iralgtmho ro eht lqutaiy fo the sreltu ecxept htat the nptapalcioi iwll ont carsh.\n");
scrollPane.setViewportView(textPane);
getContentPane().add(scrollPane);
btResetStyle.setText("Reset style");
btResetStyle.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
btResetStyleActionPerformed(evt);
}
});
getContentPane().add(btResetStyle);
pack();
// The default style, the problem happens when we reset the full document
// to it:
defaultStyle = textPane.addStyle("default", null);
}
private void btResetStyleActionPerformed(java.awt.event.ActionEvent evt) {
// When the button is pressed we reset the full document to the default
// style. In the original application this was periodically done as
// part of the spell checking
textPane.getStyledDocument().setCharacterAttributes(0, textPane.getStyledDocument().getLength(), defaultStyle, true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new DemoFrame().setVisible(true);
}
});
}
}
Reproducing the issue:
Compile and run the class above
Try to resize the frame - word wrapping works
Locate and delete the three lines I copied below
Press the Reset style button
The word wrapping turned off
* Onpti edintsy amy ont vayr oto humc
** Het angre fo the coall ption desitnsei of a igsenl pisnotte nutip ushold eb sallm. Ahtt is: teh orait of het oclla oitnp idsentise oarund any 2 ipnost lsdhou eb lmitied.
** Hte lcoal noipt deisynt ushlod otn ahencg sdduelyn (gliftyscaiinn ni a hotsr idnsteac).
EDIT 2
Using the highlighter instead of styles solved my problem, but I'm still curious what was wrong with the original approach.
This looks like the same issue I asked about here : Strange text wrapping with styled text in JTextPane with Java 7.
As far as I know, this is a bug in Java 7, and it is not documented in Oracle's Java Bug Parade. I have still not found a workaround (using the highlighter is not an option in my case).