Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.InputStream;
import java.text.Bidi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import org.apache.pdfbox.pdmodel.ContentStreamForGlyphLayoutInterface;
Expand Down Expand Up @@ -140,7 +144,7 @@ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, bool
* @throws FontFormatException if the font is bad
*/
public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream,
GlyphLayoutFontLoaderAwt.FontOptions fontOptions) throws IOException, FontFormatException
GlyphLayoutFontLoaderAwt.FontOptions fontOptions) throws IOException, FontFormatException
{
return glyphLayoutFontLoaderAwt.loadFont(pdDocument, inputStream, fontOptions);
}
Expand All @@ -154,12 +158,12 @@ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream,
* @param fontOptions options for font
*
* @return a PDType0Font font.
*
*
* @throws IOException if font can not be loaded
* @throws FontFormatException if the font is bad
*/
public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, boolean embedSubset,
GlyphLayoutFontLoaderAwt.FontOptions fontOptions) throws IOException, FontFormatException
GlyphLayoutFontLoaderAwt.FontOptions fontOptions) throws IOException, FontFormatException
{
return glyphLayoutFontLoaderAwt.loadFont(pdDocument, inputStream, embedSubset, fontOptions);
}
Expand Down Expand Up @@ -193,6 +197,60 @@ protected GlyphVector computeGlyphVector(PDType0Font font, float fontSize, Strin
return awtFont.layoutGlyphVector(fontRenderContext, chars, 0, chars.length, localFlags);
}


/**
* Class for text and Bidi-Level
*/
public static class TextAndBidiLevel {
private final String text;
private final int bidiLevel;

TextAndBidiLevel(String text, int bidiLevel){
this.text = text;
this.bidiLevel = bidiLevel;
}

public String getText() {
return text;
}

public int getBidiLevel() {
return bidiLevel;
}
}

/**
* Compute the string width for a unidirectional string
* @param font
* @param fontSize
* @param text
* @param bidiLevel
* @return string width
*/
protected float getStringWidthUni(PDType0Font font, float fontSize, String text, int bidiLevel)
{
GlyphVector glyphVector = computeGlyphVector(font, fontSize, text, bidiLevel);
Rectangle2D rect = glyphVector.getLogicalBounds();
return (float) rect.getWidth();
}

/**
* Compute the string width for a string
* @param font
* @param fontSize
* @param text
* @return string width
*/
public float getStringWidth(PDType0Font font, float fontSize, String text)
{
float width = 0f;
List<TextAndBidiLevel> textAndBidiLevels = doBidiSplittingAndReordering(text);
for (TextAndBidiLevel textAndBidiLevel: textAndBidiLevels) {
width += getStringWidthUni(font, fontSize, textAndBidiLevel.getText(), textAndBidiLevel.getBidiLevel());
}
return width;
}

/**
* Shows a text using glyph positioning (if needed)
*
Expand All @@ -205,8 +263,23 @@ protected GlyphVector computeGlyphVector(PDType0Font font, float fontSize, Strin
* @throws IllegalArgumentException if glyphs are missing
*/
@Override
public void showText(ContentStreamForGlyphLayoutInterface contentStream, PDType0Font font, float fontSize, String text) throws IOException
public void showText(ContentStreamForGlyphLayoutInterface contentStream, PDType0Font font, float fontSize, String text) throws IOException {
List<TextAndBidiLevel> textAndBidiLevels = doBidiSplittingAndReordering(text);
for (TextAndBidiLevel textAndBidiLevel: textAndBidiLevels) {
showTextUni(contentStream, font, fontSize, textAndBidiLevel.getText(), textAndBidiLevel.getBidiLevel());
}
}

/**
* Do Bidi splitting and reordering
* @param text
* @return
* @throws IOException
*/
public List<TextAndBidiLevel> doBidiSplittingAndReordering(String text)
{
ArrayList<TextAndBidiLevel> textAndBidiLevels = new ArrayList<>();

Objects.requireNonNull(text, "Text must be set");

if (Bidi.requiresBidi(text.toCharArray(), 0, text.length()))
Expand Down Expand Up @@ -236,18 +309,19 @@ public void showText(ContentStreamForGlyphLayoutInterface contentStream, PDType0
int limit = bidi.getRunLimit(index);
int bidiLevel = levels[index];
String part = text.substring(start, limit);
showTextUni(contentStream, font, fontSize, part, bidiLevel);
textAndBidiLevels.add(new TextAndBidiLevel(part, bidiLevel));
}
}
else
{
showTextUni(contentStream, font, fontSize, text, bidi.getBaseLevel());
textAndBidiLevels.add(new TextAndBidiLevel(text, bidi.getBaseLevel()));
}
}
else
{
showTextUni(contentStream, font, fontSize, text, Bidi.DIRECTION_LEFT_TO_RIGHT);
textAndBidiLevels.add(new TextAndBidiLevel(text, Bidi.DIRECTION_LEFT_TO_RIGHT));
}
return Collections.unmodifiableList(textAndBidiLevels);
}

/**
Expand All @@ -267,7 +341,7 @@ protected void showTextUni(ContentStreamForGlyphLayoutInterface contentStream, P
Objects.requireNonNull(contentStream, "contentStream must be set");

GlyphVector glyphVector = computeGlyphVector(font, fontSize, text, bidiLevel);

// check for adjustment not needed:
// glyphVector.getLayoutFlags() & FLAG_HAS_POSITION_ADJUSTMENTS is always true
// because of horizontal adjustments in every string except one character string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.awt.FontFormatException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.Bidi;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand All @@ -52,7 +53,7 @@ class GlyphLayoutLigaturesAndKerningTest extends TestBase
* Check that missing glyph is caught like in main pdfbox.
*
* @throws IOException
* @throws FontFormatException
* @throws FontFormatException
*/
@Test
void testMissingGlyph() throws IOException, FontFormatException
Expand All @@ -63,24 +64,24 @@ void testMissingGlyph() throws IOException, FontFormatException

try (PDDocument doc = new PDDocument())
{
PDType0Font lohitBengaliFont = createPdType0Font(glyphLayoutProcessor, doc, lohitBengaliPath,
new GlyphLayoutFontLoaderAwt.FontOptions());
PDType0Font lohitBengaliFont = createPdType0Font(glyphLayoutProcessor, doc, lohitBengaliPath,
new GlyphLayoutFontLoaderAwt.FontOptions());

PDPage page = new PDPage();
doc.addPage(page);
try (PDPageContentStream cs = new PDPageContentStream(doc, page))
{
cs.setGlyphLayoutProcessor(glyphLayoutProcessor);

IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () ->
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () ->
showComposites(cs, lohitBengaliFont, 1, 0, 0, "123ABC"));
assertEquals("Missing glyph in font 'Lohit Bengali' for the character 'A', codePoint: 65 (U+0041).", ex.getMessage());

// Ignore the "You did not call endText()" warning, this is because of the premature close
}
}
}

@Test
void testLigaturesAndKerning() throws IOException, FontFormatException, URISyntaxException
{
Expand All @@ -100,15 +101,15 @@ void testLigaturesAndKerning() throws IOException, FontFormatException, URISynta
PDType0Font firaFont = createPdType0Font(glyphLayoutProcessor, doc, firaPath);
PDType0Font firaLigFont = createPdType0Font(glyphLayoutProcessor, doc, firaPath,
new GlyphLayoutFontLoaderAwt.FontOptions().setLigaturesOn());

PDType0Font dejavuFont = createPdType0Font(glyphLayoutProcessor, doc, dejavuPath);

PDType0Font dejavuLigFont = createPdType0Font(glyphLayoutProcessor, doc, dejavuPath,
new GlyphLayoutFontLoaderAwt.FontOptions().setLigaturesOn());

PDType0Font dejavuKernFont = createPdType0Font(glyphLayoutProcessor, doc, dejavuPath,
new GlyphLayoutFontLoaderAwt.FontOptions().setKerningOn());

PDType0Font dejavuLigKernFont = createPdType0Font(glyphLayoutProcessor, doc, dejavuPath,
new GlyphLayoutFontLoaderAwt.FontOptions().setLigaturesOn().setKerningOn());

Expand All @@ -117,13 +118,22 @@ void testLigaturesAndKerning() throws IOException, FontFormatException, URISynta

PDType0Font lohitBengaliFont = createPdType0Font(glyphLayoutProcessor, doc, lohitBengaliPath,
new GlyphLayoutFontLoaderAwt.FontOptions());



float f1 = dejavuFont.getStringWidth(DEJAVU_STRING) * dejavuFont.getFontMatrix().getScaleX() * fontSize;
float f2 = dejavuLigKernFont.getStringWidth(DEJAVU_STRING) * dejavuLigKernFont.getFontMatrix().getScaleX() * fontSize;
float f3 = glyphLayoutProcessor.getStringWidth(dejavuFont, fontSize, DEJAVU_STRING);
float f4 = glyphLayoutProcessor.getStringWidth(dejavuLigKernFont, fontSize, DEJAVU_STRING);

System.out.println("widths: " + f1 + " " + f2);
System.out.println("widths: " + f3 + " " + f4);

PDPage page = new PDPage();
doc.addPage(page);
try (PDPageContentStream cs = new PDPageContentStream(doc, page))
{
cs.setGlyphLayoutProcessor(glyphLayoutProcessor);

float x = page.getBBox().getLowerLeftX() + fontSize;
float y = page.getBBox().getUpperRightY() - fontSize;
y = showComposites(cs, firaFont, fontSize, x, y, FIRACODE_STRING);
Expand Down Expand Up @@ -154,7 +164,7 @@ void testLigaturesAndKerning() throws IOException, FontFormatException, URISynta
* break the text into lines and show them
*/
private float showComposites(PDPageContentStream cs, PDType0Font font, float fontSize,
float x, float y, String s) throws IOException
float x, float y, String s) throws IOException
{

s = s.replaceAll("\t", " ");
Expand Down