dmolony-DiskBrowser/src/com/bytezone/diskbrowser/applefile/QuickDrawFont.java

294 lines
11 KiB
Java
Raw Permalink Normal View History

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;
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
headerSize = Utility.getShort (buffer, ptr);
2017-01-17 00:00:51 +00:00
fontDefinitionOffset = nameLength + 1 + headerSize * 2;
fontFamily = Utility.getShort (buffer, ptr + 2);
fontStyle = Utility.getShort (buffer, ptr + 4);
fontSize = Utility.getShort (buffer, ptr + 6);
2017-01-17 00:00:51 +00:00
versionMajor = buffer[ptr + 8] & 0xFF;
versionMinor = buffer[ptr + 9] & 0xFF;
extent = Utility.getShort (buffer, ptr + 10);
2017-01-17 00:00:51 +00:00
ptr = fontDefinitionOffset;
fontType = Utility.getShort (buffer, ptr);
firstChar = Utility.getShort (buffer, ptr + 2);
lastChar = Utility.getShort (buffer, ptr + 4);
widMax = Utility.getShort (buffer, ptr + 6);
kernMax = Utility.getSignedShort (buffer, ptr + 8);
nDescent = Utility.getSignedShort (buffer, ptr + 10);
fRectWidth = Utility.getShort (buffer, ptr + 12);
fRectHeight = Utility.getShort (buffer, ptr + 14);
2017-01-17 00:00:51 +00:00
owTLoc = Utility.getShort (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;
ascent = Utility.getShort (buffer, ptr + 18);
descent = Utility.getShort (buffer, ptr + 20);
leading = Utility.getShort (buffer, ptr + 22);
rowWords = Utility.getShort (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
int location = Utility.getShort (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)
{
int nextLocation = Utility.getShort (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;
int location = Utility.getShort (buffer, locationTableOffset + i * 2);
int nextLocation = Utility.getShort (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
}