Please wait. This can take some minutes ...
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.
com.itextpdf.io.font.WoffConverter Maven / Gradle / Ivy
/*
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.io.font;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.InflaterInputStream;
class WoffConverter {
private static final long woffSignature = 0x774F4646L;
public static boolean isWoffFont(byte[] woffBytes) {
return bytesToUInt(woffBytes, 0) == woffSignature;
}
public static byte[] convert(byte[] woffBytes) throws java.io.IOException {
int srcPos = 0;
int destPos = 0;
// signature
if (bytesToUInt(woffBytes, srcPos) != woffSignature) {
throw new IllegalArgumentException();
}
srcPos += 4;
byte[] flavor = new byte[4];
System.arraycopy(woffBytes, srcPos, flavor, 0, 4);
srcPos += 4;
// length
if (bytesToUInt(woffBytes, srcPos) != woffBytes.length) {
throw new IllegalArgumentException();
}
srcPos += 4;
byte[] numTables = new byte[2];
System.arraycopy(woffBytes, srcPos, numTables, 0, 2);
srcPos += 2;
// reserved
if (bytesToUShort(woffBytes, srcPos) != 0) {
throw new IllegalArgumentException();
}
srcPos += 2;
long totalSfntSize = bytesToUInt(woffBytes, srcPos);
srcPos += 4;
// majorVersion
srcPos += 2;
// minorVersion
srcPos += 2;
// metaOffset
srcPos += 4;
// metaLength
srcPos += 4;
// metaOrigLength
srcPos += 4;
// privOffset
srcPos += 4;
// privLength
srcPos += 4;
// assuming font won't be larger than 2GB
byte[] otfBytes = new byte[(int) totalSfntSize];
System.arraycopy(flavor, 0, otfBytes, destPos, 4);
destPos += 4;
System.arraycopy(numTables, 0, otfBytes, destPos, 2);
destPos += 2;
int entrySelector = -1;
int searchRange = -1;
int numTablesVal = bytesToUShort(numTables, 0);
for (int i = 0; i < 17; ++i) {
int powOfTwo = (int) Math.pow(2, i);
if (powOfTwo > numTablesVal) {
entrySelector = i;
searchRange = powOfTwo * 16;
break;
}
}
if (entrySelector < 0) {
throw new IllegalArgumentException();
}
otfBytes[destPos] = (byte) (searchRange >> 8);
otfBytes[destPos + 1] = (byte) (searchRange);
destPos += 2;
otfBytes[destPos] = (byte) (entrySelector >> 8);
otfBytes[destPos + 1] = (byte) (entrySelector);
destPos += 2;
int rangeShift = numTablesVal * 16 - searchRange;
otfBytes[destPos] = (byte) (rangeShift >> 8);
otfBytes[destPos + 1] = (byte) (rangeShift);
destPos += 2;
int outTableOffset = destPos;
List tdList = new ArrayList<>(numTablesVal);
for (int i = 0; i < numTablesVal; ++i) {
TableDirectory td = new TableDirectory();
System.arraycopy(woffBytes, srcPos, td.tag, 0, 4);
srcPos += 4;
td.offset = bytesToUInt(woffBytes, srcPos);
srcPos += 4;
if (td.offset % 4 != 0) {
throw new IllegalArgumentException();
}
td.compLength = bytesToUInt(woffBytes, srcPos);
srcPos += 4;
System.arraycopy(woffBytes, srcPos, td.origLength, 0, 4);
td.origLengthVal = bytesToUInt(td.origLength, 0);
srcPos += 4;
System.arraycopy(woffBytes, srcPos, td.origChecksum, 0, 4);
srcPos += 4;
tdList.add(td);
outTableOffset += 4*4;
}
for (TableDirectory td : tdList) {
System.arraycopy(td.tag, 0, otfBytes, destPos, 4);
destPos += 4;
System.arraycopy(td.origChecksum, 0, otfBytes, destPos, 4);
destPos += 4;
otfBytes[destPos] = (byte) (outTableOffset >> 24);
otfBytes[destPos + 1] = (byte) (outTableOffset >> 16);
otfBytes[destPos + 2] = (byte) (outTableOffset >> 8);
otfBytes[destPos + 3] = (byte) (outTableOffset);
destPos += 4;
System.arraycopy(td.origLength, 0, otfBytes, destPos, 4);
destPos += 4;
td.outOffset = outTableOffset;
outTableOffset += (int)td.origLengthVal;
if (outTableOffset % 4 != 0) {
outTableOffset += 4 - outTableOffset % 4;
}
}
if (outTableOffset != totalSfntSize) {
throw new IllegalArgumentException();
}
for (TableDirectory td : tdList) {
byte[] compressedData = new byte[(int) td.compLength];
byte[] uncompressedData;
System.arraycopy(woffBytes, (int) td.offset, compressedData, 0, (int) td.compLength);
int expectedUncompressedLen = (int) td.origLengthVal;
if (td.compLength > td.origLengthVal) {
throw new IllegalArgumentException();
}
if (td.compLength != td.origLengthVal) {
ByteArrayInputStream stream = new ByteArrayInputStream(compressedData);
InflaterInputStream zip = new InflaterInputStream(stream);
uncompressedData = new byte[expectedUncompressedLen];
int bytesRead = 0;
while (expectedUncompressedLen - bytesRead > 0) {
int readRes = zip.read(uncompressedData, bytesRead, expectedUncompressedLen - bytesRead);
if (readRes < 0) {
throw new IllegalArgumentException();
}
bytesRead += readRes;
}
if (zip.read() >= 0) {
throw new IllegalArgumentException();
}
} else {
uncompressedData = compressedData;
}
System.arraycopy(uncompressedData, 0, otfBytes, td.outOffset, expectedUncompressedLen);
}
return otfBytes;
}
private static long bytesToUInt(byte[] b, int start) {
return (b[start] & 0xFFL) << 24
| (b[start + 1] & 0xFFL) << 16
| (b[start + 2] & 0xFFL) << 8
| (b[start + 3] & 0xFFL);
}
private static int bytesToUShort(byte[] b, int start) {
return (b[start] & 0xFF) << 8
| (b[start + 1] & 0xFF);
}
private static class TableDirectory {
byte[] tag = new byte[4];
long offset;
long compLength;
byte[] origLength = new byte[4];
long origLengthVal;
byte[] origChecksum = new byte[4];
int outOffset;
}
}