All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.layout.font.selectorstrategy.AbstractFontSelectorStrategy Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 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.layout.font.selectorstrategy;

import com.itextpdf.commons.datastructures.Tuple2;
import com.itextpdf.io.font.otf.Glyph;
import com.itextpdf.io.font.otf.GlyphLine;
import com.itextpdf.io.util.TextUtil;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.layout.font.FontInfo;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.font.FontSelector;
import com.itextpdf.layout.font.FontSet;
import com.itextpdf.layout.renderer.TextPreprocessingUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * The class defines complex implementation of {@link IFontSelectorStrategy} which based on the following algorithm:
 * 1. Find first significant symbol (not whitespace or special).
 * 2. Find font which matches symbol according to passed {@link FontSelector}.
 * 3. Try to append as many symbols as possible using the current font.
 * 4. If symbol is not matched to the current font, go to step 1.
 * 

* Algorithm takes care of the case when there is no matched font for symbol or when diacritic * from another font is used (previous symbol will be processed by diacritic's font). */ public abstract class AbstractFontSelectorStrategy implements IFontSelectorStrategy { private final FontProvider fontProvider; private final FontSet additionalFonts; private final FontSelector fontSelector; /** * Creates a new instance of {@link AbstractFontSelectorStrategy}. * * @param fontProvider the font provider * @param fontSelector the font selector * @param additionalFonts the set of fonts to be used additionally to the fonts added to font provider. */ public AbstractFontSelectorStrategy(FontProvider fontProvider, FontSelector fontSelector, FontSet additionalFonts) { this.fontProvider = fontProvider; this.additionalFonts = additionalFonts; this.fontSelector = fontSelector; } /** * If it is necessary to provide a check that the best font for passed symbol equals to the current font. * Result of checking is used to split text into parts in case if inequality. * * @return {@code true} if check is needed, otherwise {@code false} */ protected abstract boolean isCurrentFontCheckRequired(); /** * {@inheritDoc} */ @Override public List> getGlyphLines(String text) { List> result = new ArrayList<>(); int index = 0; int indexDiacritic = -1; while (index < text.length()) { // Find the best font for first significant symbol PdfFont currentFont = null; int indexSignificant = nextSignificantIndex(index, text); if (indexSignificant < text.length()) { int codePoint = extractCodePoint(text, indexDiacritic == -1 ? indexSignificant : indexDiacritic); currentFont = matchFont(codePoint, fontSelector, fontProvider, additionalFonts); } List resolvedGlyphs = new ArrayList<>(); // Try to append as many symbols as possible to the current font if (currentFont != null) { Character.UnicodeScript firstScript = null; int to = indexSignificant; boolean breakRequested = false; for (int i = indexSignificant; i < text.length(); i++) { int codePoint = extractCodePoint(text, i); if (codePoint > 0xFFFF) { i++; } if (isCurrentFontCheckRequired() && (i != indexDiacritic - 1)) { if (currentFont != matchFont(codePoint, fontSelector, fontProvider, additionalFonts)) { breakRequested = true; } } if (i > indexDiacritic) { if (TextUtil.isDiacritic(codePoint)) { final PdfFont diacriticFont = matchFont(codePoint, fontSelector, fontProvider, additionalFonts); // Diacritic font must contain previous symbol, if not, don't // enable special logic for diacritic and process it as usual symbol boolean isPreviousMatchFont = i == 0 || diacriticFont == null || diacriticFont.containsGlyph(extractCodePoint(text, i - 1)); // If diacritic font equals to the current font or null, don't // enable special logic for diacritic and process it as usual symbol if (diacriticFont != null && diacriticFont != currentFont && isPreviousMatchFont) { // If it's the first diacritic in a row, we want to break to try to find a better font for // the previous letter during the next iteration if (indexDiacritic != i - 1) { breakRequested = true; } indexDiacritic = i; if (breakRequested) { to = i - 2; } } } else { indexDiacritic = -1; } } Character.UnicodeScript currScript = Character.UnicodeScript.of(codePoint); if (isSignificantUnicodeScript(currScript)) { if (firstScript == null) { firstScript = currScript; } else if (firstScript != currScript) { breakRequested = true; } } if (breakRequested) { break; } to = i; } if (to < index) { continue; } int numOfAppendedGlyphs = currentFont.appendGlyphs(text, index, to, resolvedGlyphs); index += numOfAppendedGlyphs; } // If no symbols were appended, try to append any symbols if (resolvedGlyphs.isEmpty()) { currentFont = getPdfFont(fontSelector.bestMatch(), fontProvider, additionalFonts); if (index != indexSignificant) { index += currentFont.appendGlyphs(text, index, indexSignificant - 1, resolvedGlyphs); } while (index <= indexSignificant && index < text.length()) { index += currentFont.appendAnyGlyph(text, index, resolvedGlyphs); } } GlyphLine tempGlyphLine = new GlyphLine(resolvedGlyphs); GlyphLine finalGlyphLine = TextPreprocessingUtil.replaceSpecialWhitespaceGlyphs(tempGlyphLine, currentFont); result.add(new Tuple2<>(finalGlyphLine, currentFont)); } return result; } /** * Finds the best font which matches passed symbol. * * @param codePoint the symbol to match * @param fontSelector the font selector * @param fontProvider the font provider * @param additionalFonts the addition fonts * @return font which matches the symbol */ protected PdfFont matchFont(int codePoint, FontSelector fontSelector, FontProvider fontProvider, FontSet additionalFonts) { PdfFont matchedFont = null; for (FontInfo fontInfo : fontSelector.getFonts()) { if (fontInfo.getFontUnicodeRange().contains(codePoint)) { PdfFont temptFont = getPdfFont(fontInfo, fontProvider, additionalFonts); Glyph glyph = temptFont.getGlyph(codePoint); if (null != glyph && 0 != glyph.getCode()) { matchedFont = temptFont; break; } } } return matchedFont; } private static int nextSignificantIndex(int startIndex, String text) { int nextValidChar = startIndex; for (; nextValidChar < text.length(); nextValidChar++) { if (!TextUtil.isWhitespaceOrNonPrintable(text.charAt(nextValidChar))) { break; } } return nextValidChar; } private static boolean isSignificantUnicodeScript(Character.UnicodeScript unicodeScript) { // Character.UnicodeScript.UNKNOWN will be handled as significant unicode script return unicodeScript != Character.UnicodeScript.COMMON && unicodeScript != Character.UnicodeScript.INHERITED; } private static int extractCodePoint(String text, int idx) { return TextUtil.isSurrogatePair(text, idx) ? TextUtil.convertToUtf32(text, idx) : (int) text.charAt(idx); } /** * Utility method to create PdfFont. * * @param fontInfo instance of FontInfo * * @return cached or just created PdfFont on success, otherwise null * * @see FontProvider#getPdfFont(FontInfo, FontSet) */ private static PdfFont getPdfFont(FontInfo fontInfo, FontProvider fontProvider, FontSet additionalFonts) { return fontProvider.getPdfFont(fontInfo, additionalFonts); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy