Type3Glyph.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2025 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.kernel.font;
import com.itextpdf.io.source.ByteUtils;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfStream;
import com.itextpdf.kernel.pdf.xobject.PdfXObject;
import java.nio.charset.StandardCharsets;
/**
* The content where Type3 glyphs are written to.
*/
public final class Type3Glyph extends PdfCanvas {
private static final String D_0_STR = "d0\n";
private static final String D_1_STR = "d1\n";
private static final byte[] d0 = ByteUtils.getIsoBytes(D_0_STR);
private static final byte[] d1 = ByteUtils.getIsoBytes(D_1_STR);
private float wx;
private float llx;
private float lly;
private float urx;
private float ury;
private boolean isColor = false;
/**
* Creates a Type3Glyph canvas with a new Content Stream.
*
* @param pdfDocument the document that this canvas is created for
*/
Type3Glyph(PdfDocument pdfDocument, float wx, float llx, float lly, float urx, float ury, boolean isColor) {
super((PdfStream)new PdfStream().makeIndirect(pdfDocument), null, pdfDocument);
writeMetrics(wx, llx, lly, urx, ury, isColor);
}
/**
* Creates a Type3Glyph canvas with a non-empty Content Stream.
*
* @param pdfStream {@code PdfStream} from existed document.
* @param document document to which {@code PdfStream} belongs.
*/
Type3Glyph(PdfStream pdfStream, PdfDocument document) {
super(pdfStream, null, document);
if (pdfStream.getBytes() != null) {
fillBBFromBytes(pdfStream.getBytes());
}
}
public float getWx() {
return wx;
}
public float getLlx() {
return llx;
}
public float getLly() {
return lly;
}
public float getUrx() {
return urx;
}
public float getUry() {
return ury;
}
/**
* Indicates if the glyph color specified in the glyph description or not.
*
* @return whether the glyph color is specified in the glyph description or not
*/
public boolean isColor() {
return isColor;
}
/**
* Writes the width and optionally the bounding box parameters for a glyph
*
* @param wx the advance this character will have
* @param llx the X lower left corner of the glyph bounding box. If the <CODE>isColor</CODE> option is
* <CODE>true</CODE> the value is ignored
* @param lly the Y lower left corner of the glyph bounding box. If the <CODE>isColor</CODE> option is
* <CODE>true</CODE> the value is ignored
* @param urx the X upper right corner of the glyph bounding box. If the <CODE>isColor</CODE> option is
* <CODE>true</CODE> the value is ignored
* @param ury the Y upper right corner of the glyph bounding box. If the <CODE>isColor</CODE> option is
* <CODE>true</CODE> the value is ignored
* @param isColor defines whether the glyph color is specified in the glyph description in the font.
* The consequence of value <CODE>true</CODE> is that the bounding box parameters are ignored.
*/
private void writeMetrics(float wx, float llx, float lly, float urx, float ury, boolean isColor) {
this.isColor = isColor;
this.wx = wx;
this.llx = llx;
this.lly = lly;
this.urx = urx;
this.ury = ury;
if (isColor) {
contentStream.getOutputStream()
.writeFloat(wx)
.writeSpace()
//wy
.writeFloat(0)
.writeSpace()
.writeBytes(d0);
} else {
contentStream.getOutputStream()
.writeFloat(wx)
.writeSpace()
//wy
.writeFloat(0)
.writeSpace()
.writeFloat(llx)
.writeSpace()
.writeFloat(lly)
.writeSpace()
.writeFloat(urx)
.writeSpace()
.writeFloat(ury)
.writeSpace()
.writeBytes(d1);
}
}
/**
* Creates Image XObject from image and adds it to canvas. Performs additional checks to make
* sure that we only add mask images to not colorized type 3 fonts.
*
* @param image the {@code PdfImageXObject} object
* @param a an element of the transformation matrix
* @param b an element of the transformation matrix
* @param c an element of the transformation matrix
* @param d an element of the transformation matrix
* @param e an element of the transformation matrix
* @param f an element of the transformation matrix
* @param inlineImage true if to add image as in-line.
* @return created Image XObject or null in case of in-line image (asInline = true).
*/
@Override
public PdfXObject addImageWithTransformationMatrix(ImageData image, float a, float b, float c, float d, float e, float f, boolean inlineImage) {
if (!isColor && (!image.isMask() || !(image.getBpc() == 1 || image.getBpc() > 0xff))) {
throw new PdfException("Not colorized type3 fonts accept only mask images.");
}
return super.addImageWithTransformationMatrix(image, a, b, c, d, e, f, inlineImage);
}
private void fillBBFromBytes(byte[] bytes) {
String str = new String(bytes, StandardCharsets.ISO_8859_1);
int d0Pos = str.indexOf(D_0_STR);
int d1Pos = str.indexOf(D_1_STR);
if (d0Pos != -1) {
isColor = true;
String[] bbArray = str.substring(0, d0Pos - 1).split(" ");
if (bbArray.length == 2)
this.wx = Float.parseFloat(bbArray[0]);
} else if (d1Pos != -1) {
isColor = false;
String[] bbArray = str.substring(0, d1Pos - 1).split(" ");
if (bbArray.length == 6) {
this.wx = Float.parseFloat(bbArray[0]);
this.llx = Float.parseFloat(bbArray[2]);
this.lly = Float.parseFloat(bbArray[3]);
this.urx = Float.parseFloat(bbArray[4]);
this.ury = Float.parseFloat(bbArray[5]);
}
}
}
}