2017-01-17 00:00:51 +00:00
|
|
|
package com.bytezone.diskbrowser.applefile;
|
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
import java.awt.AlphaComposite;
|
|
|
|
import java.awt.Graphics2D;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
|
|
import java.awt.image.DataBuffer;
|
2017-02-01 21:15:30 +00:00
|
|
|
import java.util.BitSet;
|
2017-01-20 04:07:08 +00:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
|
|
|
|
2017-01-17 00:00:51 +00:00
|
|
|
import com.bytezone.diskbrowser.prodos.ProdosConstants;
|
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2020-06-26 03:29:46 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.Utility;
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// see IIGS System 6.0.1 - Disk 5 Fonts.po
|
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
public class QuickDrawFont extends CharacterList
|
|
|
|
// -----------------------------------------------------------------------------------//
|
2017-01-17 00:00:51 +00:00
|
|
|
{
|
2019-11-09 13:23:30 +00:00
|
|
|
Map<Integer, QuickDrawCharacter> qdCharacters = new HashMap<> ();
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-01-17 00:00:51 +00:00
|
|
|
private boolean corrupt;
|
|
|
|
private final int fileType;
|
|
|
|
private final int auxType;
|
|
|
|
private final String fontName;
|
|
|
|
private final int headerSize;
|
|
|
|
private final int fontSize;
|
|
|
|
private final int fontFamily;
|
|
|
|
private final int fontStyle;
|
|
|
|
private final int versionMajor;
|
|
|
|
private final int versionMinor;
|
|
|
|
private final int extent;
|
|
|
|
private final int fontType;
|
|
|
|
private final int firstChar;
|
|
|
|
private final int lastChar;
|
2017-01-20 04:07:08 +00:00
|
|
|
private final int widMax;
|
|
|
|
private final int kernMax;
|
|
|
|
private final int nDescent;
|
|
|
|
private final int fRectWidth;
|
|
|
|
private final int fRectHeight;
|
|
|
|
private final int owTLoc;
|
2017-01-17 00:00:51 +00:00
|
|
|
private final int ascent;
|
|
|
|
private final int descent;
|
|
|
|
private final int leading;
|
|
|
|
private final int rowWords;
|
|
|
|
|
|
|
|
private final int totalCharacters;
|
|
|
|
|
|
|
|
private final int fontDefinitionOffset;
|
|
|
|
private final int bitImageOffset;
|
|
|
|
private final int locationTableOffset;
|
|
|
|
private final int offsetWidthTableOffset;
|
2017-02-04 00:38:27 +00:00
|
|
|
private int offsetWidthTableSize;
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2017-02-14 00:56:04 +00:00
|
|
|
private BitSet[] strike; // bit image of all characters
|
2017-02-01 21:15:30 +00:00
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-01-17 00:00:51 +00:00
|
|
|
public QuickDrawFont (String name, byte[] buffer, int fileType, int auxType)
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-01-17 00:00:51 +00:00
|
|
|
{
|
|
|
|
super (name, buffer);
|
|
|
|
|
|
|
|
assert fileType == ProdosConstants.FILE_TYPE_FONT;
|
|
|
|
this.fileType = fileType;
|
|
|
|
this.auxType = auxType;
|
|
|
|
|
2017-02-18 09:54:24 +00:00
|
|
|
if (auxType != 0)
|
|
|
|
System.out.printf ("Font aux: %04X%n", auxType);
|
|
|
|
|
2017-01-17 00:00:51 +00:00
|
|
|
fontName = HexFormatter.getPascalString (buffer, 0);
|
|
|
|
int nameLength = (buffer[0] & 0xFF);
|
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
int ptr = nameLength + 1; // start of header record
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2020-06-26 03:29:46 +00:00
|
|
|
headerSize = Utility.unsignedShort (buffer, ptr);
|
2017-01-17 00:00:51 +00:00
|
|
|
fontDefinitionOffset = nameLength + 1 + headerSize * 2;
|
|
|
|
|
2020-06-26 03:29:46 +00:00
|
|
|
fontFamily = Utility.unsignedShort (buffer, ptr + 2);
|
|
|
|
fontStyle = Utility.unsignedShort (buffer, ptr + 4);
|
|
|
|
fontSize = Utility.unsignedShort (buffer, ptr + 6);
|
2017-01-17 00:00:51 +00:00
|
|
|
versionMajor = buffer[ptr + 8] & 0xFF;
|
|
|
|
versionMinor = buffer[ptr + 9] & 0xFF;
|
2020-06-26 03:29:46 +00:00
|
|
|
extent = Utility.unsignedShort (buffer, ptr + 10);
|
2017-01-17 00:00:51 +00:00
|
|
|
|
|
|
|
ptr = fontDefinitionOffset;
|
|
|
|
|
2020-06-26 03:29:46 +00:00
|
|
|
fontType = Utility.unsignedShort (buffer, ptr);
|
|
|
|
firstChar = Utility.unsignedShort (buffer, ptr + 2);
|
|
|
|
lastChar = Utility.unsignedShort (buffer, ptr + 4);
|
|
|
|
widMax = Utility.unsignedShort (buffer, ptr + 6);
|
|
|
|
kernMax = Utility.signedShort (buffer, ptr + 8);
|
|
|
|
nDescent = Utility.signedShort (buffer, ptr + 10);
|
|
|
|
fRectWidth = Utility.unsignedShort (buffer, ptr + 12);
|
|
|
|
fRectHeight = Utility.unsignedShort (buffer, ptr + 14);
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2020-06-26 03:29:46 +00:00
|
|
|
owTLoc = Utility.unsignedShort (buffer, ptr + 16);
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
offsetWidthTableOffset = (ptr + 16) + owTLoc * 2;
|
2017-01-17 00:00:51 +00:00
|
|
|
locationTableOffset = offsetWidthTableOffset - (lastChar - firstChar + 3) * 2;
|
|
|
|
bitImageOffset = ptr + 26;
|
|
|
|
|
2020-06-26 03:29:46 +00:00
|
|
|
ascent = Utility.unsignedShort (buffer, ptr + 18);
|
|
|
|
descent = Utility.unsignedShort (buffer, ptr + 20);
|
|
|
|
leading = Utility.unsignedShort (buffer, ptr + 22);
|
|
|
|
rowWords = Utility.unsignedShort (buffer, ptr + 24);
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2017-02-04 00:38:27 +00:00
|
|
|
totalCharacters = lastChar - firstChar + 2; // includes 'missing' character
|
2017-02-01 21:15:30 +00:00
|
|
|
|
2017-02-04 00:38:27 +00:00
|
|
|
offsetWidthTableSize = (totalCharacters + 1) * 2;
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2017-02-04 00:38:27 +00:00
|
|
|
if (offsetWidthTableOffset + offsetWidthTableSize > buffer.length)
|
2017-01-17 00:00:51 +00:00
|
|
|
{
|
|
|
|
System.out.println ("*********** Bad ow length");
|
2017-02-04 00:38:27 +00:00
|
|
|
strike = null;
|
2017-01-17 00:00:51 +00:00
|
|
|
corrupt = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-14 00:56:04 +00:00
|
|
|
createStrike ();
|
|
|
|
createCharacters ();
|
2019-11-09 13:23:30 +00:00
|
|
|
// buildDisplay ();
|
|
|
|
buildImage (10, 10, 5, 5, widMax, fRectHeight,
|
|
|
|
(int) (Math.sqrt (totalCharacters) + .5));
|
2017-02-14 00:56:04 +00:00
|
|
|
}
|
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-02-14 00:56:04 +00:00
|
|
|
private void createStrike ()
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-02-14 00:56:04 +00:00
|
|
|
{
|
2017-02-04 00:38:27 +00:00
|
|
|
// create bitset for each row
|
|
|
|
strike = new BitSet[fRectHeight];
|
|
|
|
for (int i = 0; i < fRectHeight; i++)
|
|
|
|
strike[i] = new BitSet (rowWords * 16);
|
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
// convert image data to bitset
|
|
|
|
int rowLenBits = rowWords * 16; // # bits in each row
|
|
|
|
int rowLenBytes = rowWords * 2; // # bytes in each row
|
2017-02-14 00:56:04 +00:00
|
|
|
|
|
|
|
for (int row = 0; row < fRectHeight; row++) // for each row in character
|
|
|
|
for (int bit = 0; bit < rowLenBits; bit++) // for each bit in the row
|
2017-02-01 21:15:30 +00:00
|
|
|
{
|
2017-02-14 00:56:04 +00:00
|
|
|
byte b = buffer[bitImageOffset + row * rowLenBytes + bit / 8];
|
|
|
|
strike[row].set (bit, ((b & (0x80 >>> (bit % 8))) != 0));
|
2017-02-01 21:15:30 +00:00
|
|
|
}
|
2017-02-14 00:56:04 +00:00
|
|
|
}
|
2017-02-01 21:15:30 +00:00
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-02-14 00:56:04 +00:00
|
|
|
private void createCharacters ()
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-02-14 00:56:04 +00:00
|
|
|
{
|
2017-01-17 00:00:51 +00:00
|
|
|
for (int i = 0, max = totalCharacters + 1; i < max; i++)
|
|
|
|
{
|
2017-02-04 00:38:27 +00:00
|
|
|
// index into the strike
|
2020-06-26 03:29:46 +00:00
|
|
|
int location = Utility.unsignedShort (buffer, locationTableOffset + i * 2);
|
2017-01-17 00:00:51 +00:00
|
|
|
|
2017-02-04 00:38:27 +00:00
|
|
|
int j = i + 1; // next character
|
2017-01-17 00:00:51 +00:00
|
|
|
if (j < max)
|
|
|
|
{
|
2020-06-26 03:29:46 +00:00
|
|
|
int nextLocation = Utility.unsignedShort (buffer, locationTableOffset + j * 2);
|
2017-01-17 00:00:51 +00:00
|
|
|
int pixelWidth = nextLocation - location;
|
2017-01-20 04:07:08 +00:00
|
|
|
|
|
|
|
if (pixelWidth > 0)
|
2019-11-09 13:23:30 +00:00
|
|
|
{
|
|
|
|
QuickDrawCharacter c = new QuickDrawCharacter (location, pixelWidth);
|
|
|
|
qdCharacters.put (i, c);
|
|
|
|
characters.add (c);
|
|
|
|
}
|
2017-01-17 00:00:51 +00:00
|
|
|
}
|
2017-01-20 04:07:08 +00:00
|
|
|
}
|
2017-02-14 00:56:04 +00:00
|
|
|
}
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-02-14 00:56:04 +00:00
|
|
|
private void buildDisplay ()
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-02-14 00:56:04 +00:00
|
|
|
{
|
2017-02-04 00:38:27 +00:00
|
|
|
int inset = 10;
|
2017-02-01 21:15:30 +00:00
|
|
|
int spacing = 5;
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
int charsWide = (int) (Math.sqrt (totalCharacters) + .5);
|
|
|
|
int charsHigh = (totalCharacters - 1) / charsWide + 1;
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-02-04 00:38:27 +00:00
|
|
|
image = new BufferedImage (charsWide * (widMax + spacing) + inset * 2,
|
|
|
|
charsHigh * (fRectHeight + spacing) + inset * 2, BufferedImage.TYPE_BYTE_GRAY);
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
Graphics2D g2d = image.createGraphics ();
|
|
|
|
g2d.setComposite (AlphaComposite.getInstance (AlphaComposite.SRC_OVER, (float) 1.0));
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-02-04 00:38:27 +00:00
|
|
|
int x = inset;
|
|
|
|
int y = inset;
|
2017-02-01 21:15:30 +00:00
|
|
|
int count = 0;
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
for (int i = 0; i < totalCharacters + 1; i++)
|
|
|
|
{
|
2019-11-09 13:23:30 +00:00
|
|
|
int pos = qdCharacters.containsKey (i) ? i : lastChar + 1;
|
|
|
|
QuickDrawCharacter character = qdCharacters.get (pos);
|
2017-02-04 00:38:27 +00:00
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// how the character image to be drawn should be positioned with
|
2017-02-04 00:38:27 +00:00
|
|
|
// respect to the current pen location
|
2018-04-25 20:41:03 +00:00
|
|
|
// int offset = buffer[offsetWidthTableOffset + i * 2 + 1];
|
2017-02-04 00:38:27 +00:00
|
|
|
// how far the pen should be advanced after the character is drawn
|
2018-04-25 20:41:03 +00:00
|
|
|
// int width = buffer[offsetWidthTableOffset + i * 2] & 0xFF;
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
if (character != null)
|
|
|
|
g2d.drawImage (character.image, x, y, null);
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
x += widMax + spacing;
|
|
|
|
if (++count % charsWide == 0)
|
|
|
|
{
|
2017-02-04 00:38:27 +00:00
|
|
|
x = inset;
|
2017-02-01 21:15:30 +00:00
|
|
|
y += fRectHeight + spacing;
|
2017-01-20 04:07:08 +00:00
|
|
|
}
|
2017-01-17 00:00:51 +00:00
|
|
|
}
|
2017-02-01 21:15:30 +00:00
|
|
|
g2d.dispose ();
|
2017-01-17 00:00:51 +00:00
|
|
|
}
|
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-01-17 00:00:51 +00:00
|
|
|
@Override
|
|
|
|
public String getText ()
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-01-17 00:00:51 +00:00
|
|
|
{
|
|
|
|
StringBuilder text = new StringBuilder ("Name : " + name + "\n\n");
|
|
|
|
text.append ("File type : Font\n");
|
|
|
|
|
|
|
|
String auxTypeText =
|
|
|
|
auxType == 0 ? "QuickDraw Font File" : auxType == 1 ? "XX" : "??";
|
|
|
|
text.append (String.format ("Aux type : %04X (%s)%n%n", auxType, auxTypeText));
|
|
|
|
text.append (String.format ("Font name : %s%n", fontName));
|
|
|
|
text.append (String.format ("Font family : %d%n", fontFamily));
|
2017-02-04 00:38:27 +00:00
|
|
|
text.append (String.format ("File type : %d%n", fileType));
|
2017-01-17 00:00:51 +00:00
|
|
|
text.append (String.format ("Font style : %d%n", fontStyle));
|
|
|
|
text.append (String.format ("Font size : %d%n", fontSize));
|
|
|
|
text.append (String.format ("Font version : %d.%d%n", versionMajor, versionMinor));
|
|
|
|
text.append (String.format ("Font extent : %d%n%n", extent));
|
|
|
|
text.append (String.format ("Font type : %d%n", fontType));
|
|
|
|
text.append (String.format ("First char : %d%n", firstChar));
|
|
|
|
text.append (String.format ("Last char : %d%n", lastChar));
|
2017-01-20 04:07:08 +00:00
|
|
|
text.append (String.format ("Max width : %d%n", widMax));
|
|
|
|
text.append (String.format ("Max kern : %d%n", kernMax));
|
|
|
|
text.append (String.format ("Neg descent : %d%n", nDescent));
|
|
|
|
text.append (String.format ("Width : %d%n", fRectWidth));
|
|
|
|
text.append (String.format ("Height : %d%n", fRectHeight));
|
|
|
|
text.append (String.format ("O/W Offset : %04X%n", owTLoc));
|
2017-01-17 00:00:51 +00:00
|
|
|
text.append (String.format ("Ascent : %d%n", ascent));
|
|
|
|
text.append (String.format ("Descent : %d%n", descent));
|
|
|
|
text.append (String.format ("Leading : %d%n", leading));
|
|
|
|
text.append (String.format ("Row words : %d%n%n", rowWords));
|
|
|
|
|
|
|
|
if (corrupt)
|
|
|
|
{
|
|
|
|
text.append ("\nCannot interpret Font file");
|
|
|
|
return text.toString ();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < totalCharacters; i++)
|
|
|
|
{
|
2017-02-04 00:38:27 +00:00
|
|
|
int offset = buffer[offsetWidthTableOffset + i * 2 + 1] & 0xFF;
|
|
|
|
int width = buffer[offsetWidthTableOffset + i * 2] & 0xFF;
|
2017-01-17 00:00:51 +00:00
|
|
|
|
|
|
|
if (offset == 255 && width == 255)
|
|
|
|
continue;
|
|
|
|
|
2020-06-26 03:29:46 +00:00
|
|
|
int location = Utility.unsignedShort (buffer, locationTableOffset + i * 2);
|
2017-02-04 00:38:27 +00:00
|
|
|
int nextLocation =
|
2020-06-26 03:29:46 +00:00
|
|
|
Utility.unsignedShort (buffer, locationTableOffset + (i + 1) * 2);
|
2017-01-17 00:00:51 +00:00
|
|
|
int pixelWidth = nextLocation - location;
|
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
text.append (String.format (
|
|
|
|
"Char %3d, location %,5d, pixelWidth %2d. offset %,5d, width %,5d%n", i,
|
|
|
|
location, pixelWidth, offset, width));
|
2017-01-17 00:00:51 +00:00
|
|
|
}
|
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// text.append (super.getText ());
|
|
|
|
|
2017-01-17 00:00:51 +00:00
|
|
|
return text.toString ();
|
|
|
|
}
|
2017-01-20 04:07:08 +00:00
|
|
|
|
2019-11-09 13:23:30 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
class QuickDrawCharacter extends Character
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2017-01-20 04:07:08 +00:00
|
|
|
{
|
2019-11-09 13:23:30 +00:00
|
|
|
// -------------------------------------------------------------------------------//
|
|
|
|
public QuickDrawCharacter (int strikeOffset, int strikeWidth)
|
|
|
|
// -------------------------------------------------------------------------------//
|
2017-01-20 04:07:08 +00:00
|
|
|
{
|
2019-11-09 13:23:30 +00:00
|
|
|
super (strikeWidth, fRectHeight);
|
|
|
|
|
2017-01-20 04:07:08 +00:00
|
|
|
DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
|
|
|
|
|
2017-02-01 21:15:30 +00:00
|
|
|
int element = 0;
|
|
|
|
for (int row = 0; row < fRectHeight; row++)
|
2017-02-04 00:38:27 +00:00
|
|
|
for (int j = strikeOffset; j < strikeOffset + strikeWidth; j++)
|
2017-02-01 21:15:30 +00:00
|
|
|
dataBuffer.setElem (element++, strike[row].get (j) ? 255 : 0);
|
2017-01-20 04:07:08 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-17 00:00:51 +00:00
|
|
|
}
|