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

com.itextpdf.text.pdf.fonts.otf.GlyphSubstitutionTableReader Maven / Gradle / Ivy

/*
 * $Id: 8d63fe34c871ae5d1955b81ac7c5cd450a3ae3d5 $
 *
 * This file is part of the iText (R) project.
 * Copyright (c) 1998-2016 iText Group NV
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS
 *
 * 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 http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: [email protected]
 */
package com.itextpdf.text.pdf.fonts.otf;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.itextpdf.text.pdf.Glyph;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;

/**
 * 

* Parses an OpenTypeFont file and reads the Glyph Substitution Table. This table governs how two or more Glyphs should be merged * to a single Glyph. This is especially useful for Asian languages like Bangla, Hindi, etc. *

*

* This has been written according to the OPenTypeFont specifications. This may be found here. *

* * @author Palash Ray */ public class GlyphSubstitutionTableReader extends OpenTypeFontTableReader { private final int[] glyphWidthsByIndex; private final Map glyphToCharacterMap; private Map> rawLigatureSubstitutionMap; public GlyphSubstitutionTableReader(RandomAccessFileOrArray rf, int gsubTableLocation, Map glyphToCharacterMap, int[] glyphWidthsByIndex) throws IOException { super(rf, gsubTableLocation); this.glyphWidthsByIndex = glyphWidthsByIndex; this.glyphToCharacterMap = glyphToCharacterMap; } public void read() throws FontReadingException { rawLigatureSubstitutionMap = new LinkedHashMap>(); startReadingTable(); } public Map getGlyphSubstitutionMap() throws FontReadingException { Map glyphSubstitutionMap = new LinkedHashMap(); for (Integer glyphIdToReplace : rawLigatureSubstitutionMap.keySet()) { List constituentGlyphs = rawLigatureSubstitutionMap.get(glyphIdToReplace); StringBuilder chars = new StringBuilder(constituentGlyphs.size()); for (Integer constituentGlyphId : constituentGlyphs) { chars.append(getTextFromGlyph(constituentGlyphId, glyphToCharacterMap)); } Glyph glyph = new Glyph(glyphIdToReplace, glyphWidthsByIndex[glyphIdToReplace], chars.toString()); glyphSubstitutionMap.put(glyph.chars, glyph); } return Collections.unmodifiableMap(glyphSubstitutionMap); } private String getTextFromGlyph(int glyphId, Map glyphToCharacterMap) throws FontReadingException { StringBuilder chars = new StringBuilder(1); Character c = glyphToCharacterMap.get(glyphId); if (c == null) { // it means this represents a compound glyph List constituentGlyphs = rawLigatureSubstitutionMap.get(glyphId); if (constituentGlyphs == null || constituentGlyphs.isEmpty()) { throw new FontReadingException("No corresponding character or simple glyphs found for GlyphID=" + glyphId); } for (int constituentGlyphId : constituentGlyphs) { chars.append(getTextFromGlyph(constituentGlyphId, glyphToCharacterMap)); } } else { chars.append(c.charValue()); } return chars.toString(); } @Override protected void readSubTable(int lookupType, int subTableLocation) throws IOException { if (lookupType == 1) { readSingleSubstitutionSubtable(subTableLocation); } else if (lookupType == 4) { readLigatureSubstitutionSubtable(subTableLocation); } else { System.err.println("LookupType " + lookupType + " is not yet handled for " + GlyphSubstitutionTableReader.class.getSimpleName()); } } /** * LookupType 1: Single Substitution Subtable */ private void readSingleSubstitutionSubtable(int subTableLocation) throws IOException { rf.seek(subTableLocation); int substFormat = rf.readShort(); LOG.debug("substFormat=" + substFormat); if (substFormat == 1) { int coverage = rf.readShort(); LOG.debug("coverage=" + coverage); int deltaGlyphID = rf.readShort(); LOG.debug("deltaGlyphID=" + deltaGlyphID); List coverageGlyphIds = readCoverageFormat(subTableLocation + coverage); for (int coverageGlyphId : coverageGlyphIds) { int substituteGlyphId = coverageGlyphId + deltaGlyphID; rawLigatureSubstitutionMap.put(substituteGlyphId, Arrays.asList(coverageGlyphId)); } } else if (substFormat == 2) { int coverage = rf.readShort(); LOG.debug("coverage=" + coverage); int glyphCount = rf.readUnsignedShort(); int[] substitute = new int[glyphCount]; for (int k = 0; k < glyphCount; ++k) { substitute[k] = rf.readUnsignedShort(); } List coverageGlyphIds = readCoverageFormat(subTableLocation + coverage); for (int k = 0; k < glyphCount; ++k) { rawLigatureSubstitutionMap.put(substitute[k], Arrays.asList(coverageGlyphIds.get(k))); } } else { throw new IllegalArgumentException("Bad substFormat: " + substFormat); } } /** * LookupType 4: Ligature Substitution Subtable */ private void readLigatureSubstitutionSubtable(int ligatureSubstitutionSubtableLocation) throws IOException { rf.seek(ligatureSubstitutionSubtableLocation); int substFormat = rf.readShort(); LOG.debug("substFormat=" + substFormat); if (substFormat != 1) { throw new IllegalArgumentException("The expected SubstFormat is 1"); } int coverage = rf.readShort(); LOG.debug("coverage=" + coverage); int ligSetCount = rf.readShort(); List ligatureOffsets = new ArrayList(ligSetCount); for (int i = 0; i < ligSetCount; i++) { int ligatureOffset = rf.readShort(); ligatureOffsets.add(ligatureOffset); } List coverageGlyphIds = readCoverageFormat(ligatureSubstitutionSubtableLocation + coverage); if (ligSetCount != coverageGlyphIds.size()) { throw new IllegalArgumentException("According to the OpenTypeFont specifications, the coverage count should be equal to the no. of LigatureSetTables"); } for (int i = 0; i < ligSetCount; i++) { int coverageGlyphId = coverageGlyphIds.get(i); int ligatureOffset = ligatureOffsets.get(i); LOG.debug("ligatureOffset=" + ligatureOffset); readLigatureSetTable(ligatureSubstitutionSubtableLocation + ligatureOffset, coverageGlyphId); } } private void readLigatureSetTable(int ligatureSetTableLocation, int coverageGlyphId) throws IOException { rf.seek(ligatureSetTableLocation); int ligatureCount = rf.readShort(); LOG.debug("ligatureCount=" + ligatureCount); List ligatureOffsets = new ArrayList(ligatureCount); for (int i = 0; i < ligatureCount; i++) { int ligatureOffset = rf.readShort(); ligatureOffsets.add(ligatureOffset); } for (int ligatureOffset : ligatureOffsets) { readLigatureTable(ligatureSetTableLocation + ligatureOffset, coverageGlyphId); } } private void readLigatureTable(int ligatureTableLocation, int coverageGlyphId) throws IOException { rf.seek(ligatureTableLocation); int ligGlyph = rf.readShort(); LOG.debug("ligGlyph=" + ligGlyph); int compCount = rf.readShort(); List glyphIdList = new ArrayList(); glyphIdList.add(coverageGlyphId); for (int i = 0; i < compCount - 1; i++) { int glyphId = rf.readShort(); glyphIdList.add(glyphId); } LOG.debug("glyphIdList=" + glyphIdList); List previousValue = rawLigatureSubstitutionMap.put(ligGlyph, glyphIdList); if (previousValue != null) { LOG.warn("!!!!!!!!!!glyphId=" + ligGlyph + ",\npreviousValue=" + previousValue + ",\ncurrentVal=" + glyphIdList); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy