[ 
https://issues.apache.org/jira/browse/PDFBOX-3992?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Dan Fickling updated PDFBOX-3992:
---------------------------------
    Description: 
Why: The TJ operator is required to properly implement text justification in 
unicode fonts. The word spacing operator (Tw) is not sufficient because of 
note[1] from the PDF specification.

Github user backslash47 has provided a basic implementation if that is of any 
help:
https://github.com/backslash47/pdfbox/commit/3c528295b16445e58dc9fe895f78384221452be2

Thanks,
Daniel.

[1] Note: Word spacing is applied to every occurrence of the single-byte 
character code 32 in a string when using a simple font or a composite font that 
defines code 32 as a single-byte code. It does not apply to occurrences of the 
byte value 32 in multiple-byte codes. 

Example code:

{code:java}
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.util.Matrix;

public class TextWithPositioningExample {

        public static void main(String[] args) throws Exception {
                doIt("Hello World, this is a test!", "justify-example.pdf");
        }

        /**
         * This example shows how to justify a string using the 
showTextWithPositioning method.
         * First only spaces are adjusted, and then every letter.
         */
        public static void doIt(String message, String outfile) throws 
Exception {

                // the document
                try (PDDocument doc = new PDDocument();
                         InputStream is = 
PDDocument.class.getResourceAsStream("/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"))
 {

                        final float FONT_SIZE = 20.0f;

                        // Page 1
                        PDFont font = PDType0Font.load(doc, is, true);
                        //PDFont font = PDType1Font.COURIER;
                        PDPage page = new PDPage(PDRectangle.A4);
                        doc.addPage(page);
                        
                        // Get the non-justified string width in text space 
units.
                        float stringWidth = font.getStringWidth(message) * 
FONT_SIZE;
                        
                        // Get the string height in text space units.
                        float stringHeight = 
font.getFontDescriptor().getFontBoundingBox().getHeight() * FONT_SIZE;
                        
                        // Get the width we have to justify in.
                        PDRectangle pageSize = page.getMediaBox();
                        
                        PDPageContentStream contentStream = new 
PDPageContentStream(doc,
                                        page, AppendMode.OVERWRITE, false);
                
                        contentStream.beginText();
                        contentStream.setFont(font, FONT_SIZE);
                        
                        // Start at top of page.
                        
contentStream.setTextMatrix(Matrix.getTranslateInstance(0, pageSize.getHeight() 
- stringHeight / 1000f));
                        
                        // First show non-justified.
                        contentStream.showText(message);
                        
                        // Move to next line.
                        
contentStream.setTextMatrix(Matrix.getTranslateInstance(0, pageSize.getHeight() 
- ((stringHeight / 1000f) * 2)));
                        
                        // Now show word justified.
                        // The space we have to make up, in text space units.
                        float justifyWidth = ((pageSize.getWidth() * 1000f) - 
(stringWidth));
                        
                        List<Object> text = new ArrayList<>();
                        String[] parts = message.split("\\s");
                        
                        float spaceWidth = (justifyWidth / (parts.length - 1)) 
/ FONT_SIZE;

                        for (int i = 0; i < parts.length; i++) {
                                if (i != 0) {
                                        text.add(" ");
                                        // Positive values move to the left, 
negative to the right.
                                        text.add(Float.valueOf(-spaceWidth));
                                }
                                text.add(parts[i]);
                        }
                        contentStream.showTextWithPositioning(text.toArray());
                        
contentStream.setTextMatrix(Matrix.getTranslateInstance(0, pageSize.getHeight() 
- ((stringHeight / 1000f) * 3)));
                        
                        // Now show letter justified.
                        text = new ArrayList<>();
                        justifyWidth = ((pageSize.getWidth() * 1000f) - 
stringWidth);
                        float extraLetterWidth = (justifyWidth / 
(message.codePointCount(0, message.length()) - 1)) / FONT_SIZE;
                        
                        for (int i = 0; i < message.length();) {
                                if (i != 0) {
                                        
text.add(Float.valueOf(-extraLetterWidth));
                                }
                                
                                
text.add(String.valueOf(Character.toChars(message.codePointAt(i))));
                                
                                i += 
Character.charCount(message.codePointAt(i));
                        }
                        contentStream.showTextWithPositioning(text.toArray());;
                        
                        // Finish up.
                        contentStream.endText();
                        contentStream.close();

                        doc.save(outfile);
                }
        }
}

{code}


  was:
Why: The TJ operator is required to properly implement text justification in 
unicode fonts. The word spacing operator (Tw) is not sufficient because of 
note[1] from the PDF specification.

Github user backslash47 has provided a basic implementation if that is of any 
help:
https://github.com/backslash47/pdfbox/commit/3c528295b16445e58dc9fe895f78384221452be2

Thanks,
Daniel.

[1] Note: Word spacing is applied to every occurrence of the single-byte 
character code 32 in a string when using a simple font or a composite font that 
defines code 32 as a single-byte code. It does not apply to occurrences of the 
byte value 32 in multiple-byte codes. 


> Implement show text with positioning operator (TJ)
> --------------------------------------------------
>
>                 Key: PDFBOX-3992
>                 URL: https://issues.apache.org/jira/browse/PDFBOX-3992
>             Project: PDFBox
>          Issue Type: Improvement
>          Components: PDModel
>            Reporter: Dan Fickling
>         Attachments: showtextwithpositioning.patch
>
>
> Why: The TJ operator is required to properly implement text justification in 
> unicode fonts. The word spacing operator (Tw) is not sufficient because of 
> note[1] from the PDF specification.
> Github user backslash47 has provided a basic implementation if that is of any 
> help:
> https://github.com/backslash47/pdfbox/commit/3c528295b16445e58dc9fe895f78384221452be2
> Thanks,
> Daniel.
> [1] Note: Word spacing is applied to every occurrence of the single-byte 
> character code 32 in a string when using a simple font or a composite font 
> that defines code 32 as a single-byte code. It does not apply to occurrences 
> of the byte value 32 in multiple-byte codes. 
> Example code:
> {code:java}
> import java.io.InputStream;
> import java.util.ArrayList;
> import java.util.List;
> import org.apache.pdfbox.pdmodel.PDDocument;
> import org.apache.pdfbox.pdmodel.PDPage;
> import org.apache.pdfbox.pdmodel.PDPageContentStream;
> import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
> import org.apache.pdfbox.pdmodel.common.PDRectangle;
> import org.apache.pdfbox.pdmodel.font.PDFont;
> import org.apache.pdfbox.pdmodel.font.PDType0Font;
> import org.apache.pdfbox.util.Matrix;
> public class TextWithPositioningExample {
>       public static void main(String[] args) throws Exception {
>               doIt("Hello World, this is a test!", "justify-example.pdf");
>       }
>       /**
>        * This example shows how to justify a string using the 
> showTextWithPositioning method.
>        * First only spaces are adjusted, and then every letter.
>        */
>       public static void doIt(String message, String outfile) throws 
> Exception {
>               // the document
>               try (PDDocument doc = new PDDocument();
>                        InputStream is = 
> PDDocument.class.getResourceAsStream("/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"))
>  {
>                       final float FONT_SIZE = 20.0f;
>                       // Page 1
>                       PDFont font = PDType0Font.load(doc, is, true);
>                       //PDFont font = PDType1Font.COURIER;
>                       PDPage page = new PDPage(PDRectangle.A4);
>                       doc.addPage(page);
>                       
>                       // Get the non-justified string width in text space 
> units.
>                       float stringWidth = font.getStringWidth(message) * 
> FONT_SIZE;
>                       
>                       // Get the string height in text space units.
>                       float stringHeight = 
> font.getFontDescriptor().getFontBoundingBox().getHeight() * FONT_SIZE;
>                       
>                       // Get the width we have to justify in.
>                       PDRectangle pageSize = page.getMediaBox();
>                       
>                       PDPageContentStream contentStream = new 
> PDPageContentStream(doc,
>                                       page, AppendMode.OVERWRITE, false);
>               
>                       contentStream.beginText();
>                       contentStream.setFont(font, FONT_SIZE);
>                       
>                       // Start at top of page.
>                       
> contentStream.setTextMatrix(Matrix.getTranslateInstance(0, 
> pageSize.getHeight() - stringHeight / 1000f));
>                       
>                       // First show non-justified.
>                       contentStream.showText(message);
>                       
>                       // Move to next line.
>                       
> contentStream.setTextMatrix(Matrix.getTranslateInstance(0, 
> pageSize.getHeight() - ((stringHeight / 1000f) * 2)));
>                       
>                       // Now show word justified.
>                       // The space we have to make up, in text space units.
>                       float justifyWidth = ((pageSize.getWidth() * 1000f) - 
> (stringWidth));
>                       
>                       List<Object> text = new ArrayList<>();
>                       String[] parts = message.split("\\s");
>                       
>                       float spaceWidth = (justifyWidth / (parts.length - 1)) 
> / FONT_SIZE;
>                       for (int i = 0; i < parts.length; i++) {
>                               if (i != 0) {
>                                       text.add(" ");
>                                       // Positive values move to the left, 
> negative to the right.
>                                       text.add(Float.valueOf(-spaceWidth));
>                               }
>                               text.add(parts[i]);
>                       }
>                       contentStream.showTextWithPositioning(text.toArray());
>                       
> contentStream.setTextMatrix(Matrix.getTranslateInstance(0, 
> pageSize.getHeight() - ((stringHeight / 1000f) * 3)));
>                       
>                       // Now show letter justified.
>                       text = new ArrayList<>();
>                       justifyWidth = ((pageSize.getWidth() * 1000f) - 
> stringWidth);
>                       float extraLetterWidth = (justifyWidth / 
> (message.codePointCount(0, message.length()) - 1)) / FONT_SIZE;
>                       
>                       for (int i = 0; i < message.length();) {
>                               if (i != 0) {
>                                       
> text.add(Float.valueOf(-extraLetterWidth));
>                               }
>                               
>                               
> text.add(String.valueOf(Character.toChars(message.codePointAt(i))));
>                               
>                               i += 
> Character.charCount(message.codePointAt(i));
>                       }
>                       contentStream.showTextWithPositioning(text.toArray());;
>                       
>                       // Finish up.
>                       contentStream.endText();
>                       contentStream.close();
>                       doc.save(outfile);
>               }
>       }
> }
> {code}



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to