Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2023 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 .
*/
package com.itextpdf.kernel.font;
import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.io.font.FontEncoding;
import com.itextpdf.io.font.FontMetrics;
import com.itextpdf.io.font.FontNames;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.font.cmap.CMapToUnicode;
import com.itextpdf.io.font.constants.FontDescriptorFlags;
import com.itextpdf.io.font.otf.Glyph;
import com.itextpdf.io.font.otf.GlyphLine;
import com.itextpdf.io.util.ArrayUtil;
import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.io.util.StreamUtil;
import com.itextpdf.io.util.TextUtil;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfNumber;
import com.itextpdf.kernel.pdf.PdfOutputStream;
import com.itextpdf.kernel.pdf.PdfString;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class PdfSimpleFont extends PdfFont {
protected FontEncoding fontEncoding;
/**
* Forces the output of the width array. Only matters for the 14 built-in fonts.
*/
protected boolean forceWidthsOutput = false;
/**
* The array used with single byte encodings.
*/
protected byte[] usedGlyphs = new byte[PdfFont.SIMPLE_FONT_MAX_CHAR_CODE_VALUE + 1];
/**
* Currently only exists for the fonts that are parsed from the document.
* In the future, we might provide possibility to add custom mappings after a font has been created from a font file.
*/
protected CMapToUnicode toUnicode;
protected PdfSimpleFont(PdfDictionary fontDictionary) {
super(fontDictionary);
toUnicode = FontUtil.processToUnicode(fontDictionary.get(PdfName.ToUnicode));
}
protected PdfSimpleFont() {
super();
}
@Override
public boolean isBuiltWith(String fontProgram, String encoding) {
return getFontProgram().isBuiltWith(fontProgram) &&
fontEncoding.isBuiltWith(encoding);
}
@Override
public GlyphLine createGlyphLine(String content) {
List glyphs = new ArrayList<>(content.length());
if (fontEncoding.isFontSpecific()) {
for (int i = 0; i < content.length(); i++) {
Glyph glyph = fontProgram.getGlyphByCode(content.charAt(i));
if (glyph != null) {
glyphs.add(glyph);
}
}
} else {
for (int i = 0; i < content.length(); i++) {
Glyph glyph = getGlyph((int) content.charAt(i));
if (glyph != null) {
glyphs.add(glyph);
}
}
}
return new GlyphLine(glyphs);
}
@Override
public int appendGlyphs(String text, int from, int to, List glyphs) {
int processed = 0;
if (fontEncoding.isFontSpecific()) {
for (int i = from; i <= to; i++) {
Glyph glyph = fontProgram.getGlyphByCode(text.charAt(i) & 0xFF);
if (glyph != null) {
glyphs.add(glyph);
processed++;
} else {
break;
}
}
} else {
for (int i = from; i <= to; i++) {
Glyph glyph = getGlyph((int) text.charAt(i));
if (glyph != null && (containsGlyph(glyph.getUnicode()) || isAppendableGlyph(glyph))) {
glyphs.add(glyph);
processed++;
} else if (glyph == null && TextUtil.isWhitespaceOrNonPrintable((int) text.charAt(i))) {
processed++;
} else {
break;
}
}
}
return processed;
}
@Override
public int appendAnyGlyph(String text, int from, List glyphs) {
Glyph glyph;
if (fontEncoding.isFontSpecific()) {
glyph = fontProgram.getGlyphByCode(text.charAt(from));
} else {
glyph = getGlyph((int) text.charAt(from));
}
if (glyph != null) {
glyphs.add(glyph);
}
return 1;
}
/**
* Checks whether the glyph is appendable, i.e. has valid unicode and code values.
*
* @param glyph not-null {@link Glyph}
*/
private boolean isAppendableGlyph(Glyph glyph) {
// If font is specific and glyph.getCode() = 0, unicode value will be also 0.
// Character.isIdentifierIgnorable(0) gets true.
return glyph.getCode() > 0 || TextUtil.isWhitespaceOrNonPrintable(glyph.getUnicode());
}
/**
* Get the font encoding.
*
* @return the {@link FontEncoding}
*/
public FontEncoding getFontEncoding() {
return fontEncoding;
}
/**
* Get the mapping of character codes to unicode values based on /ToUnicode entry of font dictionary.
*
* @return the {@link CMapToUnicode} built based on /ToUnicode, or null if /ToUnicode is not available
*/
public CMapToUnicode getToUnicode() {
return toUnicode;
}
@Override
public byte[] convertToBytes(String text) {
byte[] bytes = fontEncoding.convertToBytes(text);
for (byte b : bytes) {
usedGlyphs[b & 0xff] = 1;
}
return bytes;
}
@Override
public byte[] convertToBytes(GlyphLine glyphLine) {
if (glyphLine != null) {
byte[] bytes = new byte[glyphLine.size()];
int ptr = 0;
if (fontEncoding.isFontSpecific()) {
for (int i = 0; i < glyphLine.size(); i++) {
bytes[ptr++] = (byte) glyphLine.get(i).getCode();
}
} else {
for (int i = 0; i < glyphLine.size(); i++) {
if (fontEncoding.canEncode(glyphLine.get(i).getUnicode())) {
bytes[ptr++] = (byte) fontEncoding.convertToByte(glyphLine.get(i).getUnicode());
}
}
}
bytes = ArrayUtil.shortenArray(bytes, ptr);
for (byte b : bytes) {
usedGlyphs[b & 0xff] = 1;
}
return bytes;
} else {
return EMPTY_BYTES;
}
}
@Override
public byte[] convertToBytes(Glyph glyph) {
byte[] bytes = new byte[1];
if (fontEncoding.isFontSpecific()) {
bytes[0] = (byte) glyph.getCode();
} else {
if (fontEncoding.canEncode(glyph.getUnicode())) {
bytes[0] = (byte) fontEncoding.convertToByte(glyph.getUnicode());
} else {
return EMPTY_BYTES;
}
}
usedGlyphs[bytes[0] & 0xff] = 1;
return bytes;
}
@Override
public void writeText(GlyphLine text, int from, int to, PdfOutputStream stream) {
byte[] bytes = new byte[to - from + 1];
int ptr = 0;
if (fontEncoding.isFontSpecific()) {
for (int i = from; i <= to; i++) {
bytes[ptr++] = (byte) text.get(i).getCode();
}
} else {
for (int i = from; i <= to; i++) {
Glyph glyph = text.get(i);
if (fontEncoding.canEncode(glyph.getUnicode())) {
bytes[ptr++] = (byte) fontEncoding.convertToByte(glyph.getUnicode());
}
}
}
bytes = ArrayUtil.shortenArray(bytes, ptr);
for (byte b : bytes) {
usedGlyphs[b & 0xff] = 1;
}
StreamUtil.writeEscapedString(stream, bytes);
}
@Override
public void writeText(String text, PdfOutputStream stream) {
StreamUtil.writeEscapedString(stream, convertToBytes(text));
}
@Override
public String decode(PdfString content) {
return decodeIntoGlyphLine(content).toString();
}
/**
* {@inheritDoc}
*/
@Override
public GlyphLine decodeIntoGlyphLine(PdfString content) {
List glyphs = new ArrayList<>(content.getValue().length());
appendDecodedCodesToGlyphsList(glyphs, content);
return new GlyphLine(glyphs);
}
/**
* {@inheritDoc}
*/
@Override
public boolean appendDecodedCodesToGlyphsList(List list, PdfString characterCodes) {
boolean allCodesDecoded = true;
FontEncoding enc = getFontEncoding();
byte[] contentBytes = characterCodes.getValueBytes();
for (byte b : contentBytes) {
int code = b & 0xff;
Glyph glyph = null;
CMapToUnicode toUnicodeCMap = getToUnicode();
if (toUnicodeCMap != null && toUnicodeCMap.lookup(code) != null
&& (glyph = getFontProgram().getGlyphByCode(code)) != null) {
if (!Arrays.equals(toUnicodeCMap.lookup(code), glyph.getChars())) {
// Copy the glyph because the original one may be reused (e.g. standard Helvetica font program)
glyph = new Glyph(glyph);
glyph.setChars(toUnicodeCMap.lookup(code));
}
} else {
int uni = enc.getUnicode(code);
if (uni > -1) {
glyph = getGlyph(uni);
} else if (enc.getBaseEncoding() == null) {
glyph = getFontProgram().getGlyphByCode(code);
}
}
if (glyph != null) {
list.add(glyph);
} else {
Logger logger = LoggerFactory.getLogger(this.getClass());
if (logger.isWarnEnabled()) {
logger.warn(MessageFormatUtil.format(IoLogMessageConstant.COULD_NOT_FIND_GLYPH_WITH_CODE, code));
}
allCodesDecoded = false;
}
}
return allCodesDecoded;
}
@Override
public float getContentWidth(PdfString content) {
float width = 0;
GlyphLine glyphLine = decodeIntoGlyphLine(content);
for (int i = glyphLine.start; i < glyphLine.end; i++) {
width += glyphLine.get(i).getWidth();
}
return width;
}
/**
* Gets the state of the property.
*
* @return value of property forceWidthsOutput
*/
public boolean isForceWidthsOutput() {
return forceWidthsOutput;
}
/**
* Set to {@code true} to force the generation of the widths array.
*
* @param forceWidthsOutput {@code true} to force the generation of the widths array
*/
public void setForceWidthsOutput(boolean forceWidthsOutput) {
this.forceWidthsOutput = forceWidthsOutput;
}
protected void flushFontData(String fontName, PdfName subtype) {
getPdfObject().put(PdfName.Subtype, subtype);
if (fontName != null && fontName.length() > 0) {
getPdfObject().put(PdfName.BaseFont, new PdfName(fontName));
}
int firstChar;
int lastChar;
for (firstChar = 0; firstChar <= PdfFont.SIMPLE_FONT_MAX_CHAR_CODE_VALUE; ++firstChar) {
if (usedGlyphs[firstChar] != 0) break;
}
for (lastChar = PdfFont.SIMPLE_FONT_MAX_CHAR_CODE_VALUE; lastChar >= firstChar; --lastChar) {
if (usedGlyphs[lastChar] != 0) break;
}
if (firstChar > PdfFont.SIMPLE_FONT_MAX_CHAR_CODE_VALUE) {
firstChar = PdfFont.SIMPLE_FONT_MAX_CHAR_CODE_VALUE;
lastChar = PdfFont.SIMPLE_FONT_MAX_CHAR_CODE_VALUE;
}
if (!isSubset() || !isEmbedded()) {
firstChar = 0;
lastChar = usedGlyphs.length - 1;
for (int k = 0; k < usedGlyphs.length; ++k) {
// remove unsupported by encoding values in case custom encoding.
// save widths information in case standard pdf encodings (winansi or macroman)
if (fontEncoding.canDecode(k)) {
usedGlyphs[k] = 1;
} else if (!fontEncoding.hasDifferences() && fontProgram.getGlyphByCode(k) != null) {
usedGlyphs[k] = 1;
} else {
usedGlyphs[k] = 0;
}
}
}
if (fontEncoding.hasDifferences()) {
// trim range of symbols
for (int k = firstChar; k <= lastChar; ++k) {
if (!FontEncoding.NOTDEF.equals(fontEncoding.getDifference(k))) {
firstChar = k;
break;
}
}
for (int k = lastChar; k >= firstChar; --k) {
if (!FontEncoding.NOTDEF.equals(fontEncoding.getDifference(k))) {
lastChar = k;
break;
}
}
PdfDictionary enc = new PdfDictionary();
enc.put(PdfName.Type, PdfName.Encoding);
PdfArray diff = new PdfArray();
boolean gap = true;
for (int k = firstChar; k <= lastChar; ++k) {
if (usedGlyphs[k] != 0) {
if (gap) {
diff.add(new PdfNumber(k));
gap = false;
}
diff.add(new PdfName(fontEncoding.getDifference(k)));
} else {
gap = true;
}
}
enc.put(PdfName.Differences, diff);
getPdfObject().put(PdfName.Encoding, enc);
} else if (!fontEncoding.isFontSpecific()) {
getPdfObject().put(PdfName.Encoding, PdfEncodings.CP1252.equals(fontEncoding.getBaseEncoding())
? PdfName.WinAnsiEncoding
: PdfName.MacRomanEncoding);
}
if (isForceWidthsOutput() || !isBuiltInFont() || fontEncoding.hasDifferences()) {
getPdfObject().put(PdfName.FirstChar, new PdfNumber(firstChar));
getPdfObject().put(PdfName.LastChar, new PdfNumber(lastChar));
PdfArray wd = buildWidthsArray(firstChar, lastChar);
getPdfObject().put(PdfName.Widths, wd);
}
PdfDictionary fontDescriptor = !isBuiltInFont() ? getFontDescriptor(fontName) : null;
if (fontDescriptor != null) {
getPdfObject().put(PdfName.FontDescriptor, fontDescriptor);
if (fontDescriptor.getIndirectReference() != null) {
fontDescriptor.flush();
}
}
}
/**
* Indicates that the font is built in, i.e. it is one of the 14 Standard fonts
* @return {@code true} in case the font is a Standard font and {@code false} otherwise
*/
protected boolean isBuiltInFont() {
return false;
}
/**
* Generates the font descriptor for this font or {@code null} if it is one of the 14 built in fonts.
*
* @return the PdfDictionary containing the font descriptor or {@code null}.
*/
@Override
protected PdfDictionary getFontDescriptor(String fontName) {
assert fontName != null && fontName.length() > 0;
FontMetrics fontMetrics = fontProgram.getFontMetrics();
FontNames fontNames = fontProgram.getFontNames();
PdfDictionary fontDescriptor = new PdfDictionary();
makeObjectIndirect(fontDescriptor);
fontDescriptor.put(PdfName.Type, PdfName.FontDescriptor);
fontDescriptor.put(PdfName.FontName, new PdfName(fontName));
fontDescriptor.put(PdfName.Ascent, new PdfNumber(fontMetrics.getTypoAscender()));
fontDescriptor.put(PdfName.CapHeight, new PdfNumber(fontMetrics.getCapHeight()));
fontDescriptor.put(PdfName.Descent, new PdfNumber(fontMetrics.getTypoDescender()));
fontDescriptor.put(PdfName.FontBBox, new PdfArray(ArrayUtil.cloneArray(fontMetrics.getBbox())));
fontDescriptor.put(PdfName.ItalicAngle, new PdfNumber(fontMetrics.getItalicAngle()));
fontDescriptor.put(PdfName.StemV, new PdfNumber(fontMetrics.getStemV()));
if (fontMetrics.getXHeight() > 0) {
fontDescriptor.put(PdfName.XHeight, new PdfNumber(fontMetrics.getXHeight()));
}
if (fontMetrics.getStemH() > 0) {
fontDescriptor.put(PdfName.StemH, new PdfNumber(fontMetrics.getStemH()));
}
if (fontNames.getFontWeight() > 0) {
fontDescriptor.put(PdfName.FontWeight, new PdfNumber(fontNames.getFontWeight()));
}
if (fontNames.getFamilyName() != null && fontNames.getFamilyName().length > 0 && fontNames.getFamilyName()[0].length >= 4) {
fontDescriptor.put(PdfName.FontFamily, new PdfString(fontNames.getFamilyName()[0][3]));
}
//add font stream and flush it immediately
addFontStream(fontDescriptor);
int flags = fontProgram.getPdfFontFlags();
// reset both flags
flags &= ~(FontDescriptorFlags.Symbolic | FontDescriptorFlags.Nonsymbolic);
// set fontSpecific based on font encoding
flags |= fontEncoding.isFontSpecific() ?
FontDescriptorFlags.Symbolic : FontDescriptorFlags.Nonsymbolic;
fontDescriptor.put(PdfName.Flags, new PdfNumber(flags));
return fontDescriptor;
}
protected PdfArray buildWidthsArray(int firstChar, int lastChar) {
PdfArray wd = new PdfArray();
for (int k = firstChar; k <= lastChar; ++k) {
if (usedGlyphs[k] == 0) {
wd.add(new PdfNumber(0));
} else {
int uni = fontEncoding.getUnicode(k);
Glyph glyph = uni > -1 ? getGlyph(uni) : fontProgram.getGlyphByCode(k);
wd.add(new PdfNumber(glyph != null ? glyph.getWidth() : 0));
}
}
return wd;
}
protected abstract void addFontStream(PdfDictionary fontDescriptor);
protected void setFontProgram(T fontProgram) {
this.fontProgram = fontProgram;
}
}