From c8397009fa9d0f87dec7a19c09da009c3a434347 Mon Sep 17 00:00:00 2001 From: Denis Molony Date: Tue, 17 Jan 2017 11:00:51 +1100 Subject: [PATCH] QuickDraw fonts --- .../diskbrowser/applefile/QuickDrawFont.java | 253 ++++++++++++++++++ .../diskbrowser/applefile/SimpleText.java | 73 ++--- .../diskbrowser/prodos/FileEntry.java | 8 + .../diskbrowser/prodos/ProdosConstants.java | 62 +++-- .../diskbrowser/utilities/HexFormatter.java | 19 +- 5 files changed, 343 insertions(+), 72 deletions(-) create mode 100644 src/com/bytezone/diskbrowser/applefile/QuickDrawFont.java diff --git a/src/com/bytezone/diskbrowser/applefile/QuickDrawFont.java b/src/com/bytezone/diskbrowser/applefile/QuickDrawFont.java new file mode 100644 index 0000000..1438263 --- /dev/null +++ b/src/com/bytezone/diskbrowser/applefile/QuickDrawFont.java @@ -0,0 +1,253 @@ +package com.bytezone.diskbrowser.applefile; + +import com.bytezone.diskbrowser.prodos.ProdosConstants; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +public class QuickDrawFont extends AbstractFile +{ + 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; + private final int maxWidth; + private final int maxKern; + private final int negativeDescent; + private final int rectangleWidth; + private final int rectangleHeight; + private final int offsetToOffsetWidthTable; + private final int ascent; + private final int descent; + private final int leading; + private final int rowWords; + + private final int totalCharacters; + private final String[] imageLines; + + private final byte[] bitImage; + private final byte[] locationTable; + private final byte[] offsetWidthTable; + + private final int fontDefinitionOffset; + private final int bitImageOffset; + private final int locationTableOffset; + private final int offsetWidthTableOffset; + + public QuickDrawFont (String name, byte[] buffer, int fileType, int auxType) + { + super (name, buffer); + + assert fileType == ProdosConstants.FILE_TYPE_FONT; + this.fileType = fileType; + this.auxType = auxType; + + fontName = HexFormatter.getPascalString (buffer, 0); + int nameLength = (buffer[0] & 0xFF); + + int ptr = nameLength + 1; + + headerSize = HexFormatter.getShort (buffer, ptr); + fontDefinitionOffset = nameLength + 1 + headerSize * 2; + + fontFamily = HexFormatter.getShort (buffer, ptr + 2); + fontStyle = HexFormatter.getShort (buffer, ptr + 4); + fontSize = HexFormatter.getShort (buffer, ptr + 6); + versionMajor = buffer[ptr + 8] & 0xFF; + versionMinor = buffer[ptr + 9] & 0xFF; + extent = HexFormatter.getShort (buffer, ptr + 10); + + ptr = fontDefinitionOffset; + + fontType = HexFormatter.getShort (buffer, ptr); + firstChar = HexFormatter.getShort (buffer, ptr + 2); + lastChar = HexFormatter.getShort (buffer, ptr + 4); + maxWidth = HexFormatter.getShort (buffer, ptr + 6); + maxKern = HexFormatter.getShort (buffer, ptr + 8); + negativeDescent = HexFormatter.getShort (buffer, ptr + 10); + rectangleWidth = HexFormatter.getShort (buffer, ptr + 12); + rectangleHeight = HexFormatter.getShort (buffer, ptr + 14); + imageLines = new String[rectangleHeight]; + + offsetToOffsetWidthTable = HexFormatter.getShort (buffer, ptr + 16); + + offsetWidthTableOffset = (ptr + 16) + offsetToOffsetWidthTable * 2; + locationTableOffset = offsetWidthTableOffset - (lastChar - firstChar + 3) * 2; + bitImageOffset = ptr + 26; + + ascent = HexFormatter.getShort (buffer, ptr + 18); + descent = HexFormatter.getShort (buffer, ptr + 20); + leading = HexFormatter.getShort (buffer, ptr + 22); + rowWords = HexFormatter.getShort (buffer, ptr + 24); + + totalCharacters = lastChar - firstChar + 2; // includes missing character + + bitImage = new byte[rowWords * 2 * rectangleHeight]; // should use java bits + locationTable = new byte[(totalCharacters + 1) * 2]; + offsetWidthTable = new byte[(totalCharacters + 1) * 2]; + + if (false) + { + System.out.printf ("Buffer length : %d%n", buffer.length); + System.out.printf ("Total chars : %d%n", totalCharacters); + System.out.printf ("owtable offset : %d%n", offsetWidthTableOffset); + System.out.printf ("owtable size : %d%n", offsetWidthTable.length); + } + + if (offsetWidthTableOffset + offsetWidthTable.length > buffer.length) + { + System.out.println ("*********** Bad ow length"); + corrupt = true; + return; + } + + System.arraycopy (buffer, bitImageOffset, bitImage, 0, bitImage.length); + System.arraycopy (buffer, locationTableOffset, locationTable, 0, + locationTable.length); + System.arraycopy (buffer, offsetWidthTableOffset, offsetWidthTable, 0, + offsetWidthTable.length); + + for (int i = 0; i < rectangleHeight; i++) + { + int rowOffset = i * rowWords * 2; + + StringBuilder bits = new StringBuilder (); + for (int j = rowOffset; j < rowOffset + rowWords * 2; j++) + bits.append (HexFormatter.getBitString (bitImage[j])); + imageLines[i] = bits.toString ().replaceAll ("1", "#"); + } + + // System.out.println ("\n Location table o/w table\n"); + for (int i = 0, max = totalCharacters + 1; i < max; i++) + { + int location = HexFormatter.getShort (locationTable, i * 2); + int offset = offsetWidthTable[i * 2] & 0xFF; + int width = offsetWidthTable[i * 2 + 1] & 0xFF; + + int j = i + 1; + if (j < max) + { + int nextLocation = HexFormatter.getShort (locationTable, j * 2); + int pixelWidth = nextLocation - location; + // System.out.printf ("%3d %04X %04X %2d %02X %02X%n", i, location, + // nextLocation, pixelWidth, offset, width); + if (pixelWidth < 0) + { + System.out.println ("*********** Bad pixelWidth"); + corrupt = true; + return; + } + } + // else + // System.out.printf ("%3d %04X %n", i, location); + } + } + + @Override + public String getText () + { + 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)); + 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)); + text.append (String.format ("Max width : %d%n", maxWidth)); + text.append (String.format ("Max kern : %d%n", maxKern)); + text.append (String.format ("Neg descent : %d%n", negativeDescent)); + text.append (String.format ("Width : %d%n", rectangleWidth)); + text.append (String.format ("Height : %d%n", rectangleHeight)); + text.append (String.format ("O/W Offset : %d%n", offsetToOffsetWidthTable)); + 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++) + { + int offset = offsetWidthTable[i * 2] & 0xFF; + int width = offsetWidthTable[i * 2 + 1] & 0xFF; + + if (offset == 255 && width == 255) + continue; + + int location = HexFormatter.getShort (locationTable, i * 2); + int nextLocation = HexFormatter.getShort (locationTable, (i + 1) * 2); + int pixelWidth = nextLocation - location; + + text.append (String.format ("Char %3d %,5d %2d %,5d %,5d%n", i, location, + pixelWidth, offset, width)); + + if (pixelWidth > 0 && location + pixelWidth < imageLines[0].length ()) + for (int j = 0; j < rectangleHeight; j++) + { + for (int w = 0; w < width; w++) + text.append (' '); + text.append (imageLines[j].substring (location, location + pixelWidth)); + text.append ("\n"); + } + } + + if (false) + { + text.append ("\n\n"); + for (int i = 0; i < rectangleHeight; i++) + { + text.append (String.format ("Row: %d%n", i)); + int rowOffset = i * rowWords * 2; + String line = HexFormatter.format (bitImage, rowOffset, rowWords * 2, + bitImageOffset + rowOffset); + text.append (line); + text.append ("\n\n"); + } + + text.append ("\n\n"); + text.append (HexFormatter.format (locationTable, 0, locationTable.length, + locationTableOffset)); + + text.append ("\n\n"); + text.append (HexFormatter.format (offsetWidthTable, 0, offsetWidthTable.length, + offsetWidthTableOffset)); + + text.append ("\n\n"); + for (int i = 0; i < totalCharacters; i++) + { + int location = HexFormatter.getShort (locationTable, i * 2); + text.append (String.format ("%3d %04X %,7d%n", i, location, location)); + } + + text.append ("\n\n"); + for (int i = 0; i < totalCharacters; i++) + { + int offset = offsetWidthTable[i * 2] & 0xFF; + int width = offsetWidthTable[i * 2 + 1] & 0xFF; + text.append (String.format ("%3d %02X %02X%n", i, offset, width)); + } + } + + return text.toString (); + } +} \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/applefile/SimpleText.java b/src/com/bytezone/diskbrowser/applefile/SimpleText.java index 5143df1..427bacc 100755 --- a/src/com/bytezone/diskbrowser/applefile/SimpleText.java +++ b/src/com/bytezone/diskbrowser/applefile/SimpleText.java @@ -3,44 +3,47 @@ package com.bytezone.diskbrowser.applefile; public class SimpleText extends AbstractFile { - public SimpleText (String name, byte[] buffer) - { - super (name, buffer); - } + public SimpleText (String name, byte[] buffer) + { + super (name, buffer); + } - @Override - public String getText () - { - StringBuilder text = new StringBuilder (); + @Override + public String getText () + { + StringBuilder text = new StringBuilder (); - text.append ("Name : " + name + "\n"); - text.append (String.format ("End of file : %,8d%n%n", buffer.length)); + text.append ("Name : " + name + "\n"); + text.append (String.format ("End of file : %,8d%n%n", buffer.length)); - int ptr = 0; - while (ptr < buffer.length) - { - String line = getLine (ptr); - text.append (line + "\n"); - ptr += line.length () + 1; - if (ptr < buffer.length && buffer[ptr] == 0x0A) - ptr++; - } - return text.toString (); - } + int ptr = 0; + while (ptr < buffer.length) + { + String line = getLine (ptr); + text.append (line + "\n"); + ptr += line.length () + 1; + if (ptr < buffer.length && buffer[ptr] == 0x0A) + ptr++; + } + return text.toString (); + } - private String getLine (int ptr) - { - StringBuilder line = new StringBuilder (); - while (ptr < buffer.length && buffer[ptr] != 0x0D) - line.append ((char) buffer[ptr++]); - return line.toString (); - } + private String getLine (int ptr) + { + StringBuilder line = new StringBuilder (); - public static boolean isHTML (byte[] buffer) - { - String text = new String (buffer, 0, buffer.length); - if (text.indexOf ("HTML") > 0 || text.indexOf ("html") > 0) - return true; - return false; - } + // added check for 0x00 eol 17/01/17 + while (ptr < buffer.length && buffer[ptr] != 0x0D && buffer[ptr] != 0x00) + line.append ((char) buffer[ptr++]); + + return line.toString (); + } + + public static boolean isHTML (byte[] buffer) + { + String text = new String (buffer, 0, buffer.length); + if (text.indexOf ("HTML") > 0 || text.indexOf ("html") > 0) + return true; + return false; + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/prodos/FileEntry.java b/src/com/bytezone/diskbrowser/prodos/FileEntry.java index 5184d75..d9ab540 100755 --- a/src/com/bytezone/diskbrowser/prodos/FileEntry.java +++ b/src/com/bytezone/diskbrowser/prodos/FileEntry.java @@ -321,6 +321,11 @@ class FileEntry extends CatalogEntry implements ProdosConstants case FILE_TYPE_APPLETALK: file = new DefaultAppleFile (name + " (Appletalk file)", buffer); break; + case FILE_TYPE_GWP: + // using full buffer because the ENDFILE can be 512 for multi-block files + // SimpleText will now terminate at 0x00 (see Fonts.po) + file = new SimpleText (name, buffer); + break; case FILE_TYPE_AWP: file = new AppleworksWPFile (name + " (Appleworks Word Processor)", buffer); break; @@ -342,6 +347,9 @@ class FileEntry extends CatalogEntry implements ProdosConstants case FILE_TYPE_PIC: file = new OriginalHiResImage (name, exactBuffer, fileType, auxType); break; + case FILE_TYPE_FONT: + file = new QuickDrawFont (name, exactBuffer, fileType, auxType); + break; default: System.out.format ("Unknown file type : %02X%n", fileType); if (fileType == 0xB3) diff --git a/src/com/bytezone/diskbrowser/prodos/ProdosConstants.java b/src/com/bytezone/diskbrowser/prodos/ProdosConstants.java index 17c5e40..6a4be4e 100755 --- a/src/com/bytezone/diskbrowser/prodos/ProdosConstants.java +++ b/src/com/bytezone/diskbrowser/prodos/ProdosConstants.java @@ -9,19 +9,21 @@ public interface ProdosConstants int FILE_TYPE_ADB = 0x19; int FILE_TYPE_AWP = 0x1A; int FILE_TYPE_ASP = 0x1B; + int FILE_TYPE_GWP = 0x50; int FILE_TYPE_ASM_SOURCE = 0xB0; int FILE_TYPE_ASM_OBJECT = 0xB1; int FILE_TYPE_FORKED_FILE = 0xB3; // S16 int FILE_TYPE_PNT = 0xC0; int FILE_TYPE_PIC = 0xC1; + int FILE_TYPE_FONT = 0xC8; int FILE_TYPE_ICN = 0xCA; + int FILE_TYPE_APPLETALK = 0xE2; int FILE_TYPE_INTEGER_BASIC = 0xFA; int FILE_TYPE_INTEGER_BASIC_VARS = 0xFB; int FILE_TYPE_APPLESOFT_BASIC = 0xFC; int FILE_TYPE_APPLESOFT_BASIC_VARS = 0xFD; int FILE_TYPE_RELOCATABLE = 0xFE; int FILE_TYPE_SYS = 0xFF; - int FILE_TYPE_APPLETALK = 0xE2; int TYPE_DIRECTORY_HEADER = 15; int TYPE_SUBDIRECTORY_HEADER = 14; @@ -33,31 +35,39 @@ public interface ProdosConstants int TYPE_SEEDLING = 1; int TYPE_FREE = 0; - String[] fileTypes = - { "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", "FOT", "BA3", "DA3", - "WPF", "SOS", "$0D", "$0E", "DIR", "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", - "PFS", "$17", "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", "TDM", - "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", "8SC", "8OB", - "8IC", "8LD", "P8C", "$2F", "$30", "$31", "$32", "$33", "$34", "$35", "$36", - "$37", "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", "DIC", "OCR", - "FTD", "$43", "$44", "$45", "$46", "$47", "$48", "$49", "$4A", "$4B", "$4C", - "$4D", "$4E", "$4F", "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", - "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", "$60", "$61", "$62", - "$63", "$64", "$65", "$66", "$67", "$68", "$69", "$6A", "BIO", "$6C", "TDR", - "PRE", "HDV", "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", "$78", - "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", "GES", "GEA", "GEO", "GED", - "GEF", "GEP", "GEI", "GEX", "$88", "GEV", "$8A", "GEC", "GEK", "GEW", "$8E", - "$8F", "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", "$98", "$99", - "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", "WP ", "$A1", "$A2", "$A3", "$A4", - "$A5", "$A6", "$A7", "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", - "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", "NDA", "CDA", "TOL", - "DVR", "LDF", "FST", "$BE", "DOC", "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", - "SCR", "CDV", "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", "$D0", - "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", "SND", "$D9", "$DA", "DBM", - "$DC", "DDD", "$DE", "$DF", "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", - "$E7", "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS", "CMD", "$F1", - "$F2", "$F3", "$F4", "$F5", "$F6", "$F7", "$F8", "OS ", "INT", "IVR", "BAS", - "VAR", "REL", "SYS" }; + String[] fileTypes = { // + "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", // + "FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", // + "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", // + "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", // + "TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", // + "$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", // + "$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", // + "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", // + "DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", // + "$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", // + "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", // + "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", // + "$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", // + "$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", // + "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", // + "$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", // + "GES", "GEA", "GEO", "GED", "GEF", "GEP", "GEI", "GEX", // + "$88", "GEV", "$8A", "GEC", "GEK", "GEW", "$8E", "$8F", // + "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", // + "$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", // + "WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", // + "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", // + "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", // + "NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", // + "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", // + "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", // + "$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", // + "SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", // + "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", // + "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS", // + "CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7", // + "$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS" }; int ENTRY_SIZE = 39; int ENTRIES_PER_BLOCK = 13; diff --git a/src/com/bytezone/diskbrowser/utilities/HexFormatter.java b/src/com/bytezone/diskbrowser/utilities/HexFormatter.java index 3f86884..5534648 100755 --- a/src/com/bytezone/diskbrowser/utilities/HexFormatter.java +++ b/src/com/bytezone/diskbrowser/utilities/HexFormatter.java @@ -60,19 +60,9 @@ public class HexFormatter line.append ("\n"); // print offset - for (int temp = i + startingAddress, max = 65536; max > 0; max /= 16) - { - if (temp >= max) - { - line.append (hex[temp / max]); - temp %= max; - } - else - line.append ("0"); - } + line.append (String.format ("%05X : ", (startingAddress + i - offset))); // print hex values - line.append (" : "); StringBuffer trans = new StringBuffer (); StringBuffer hexLine = new StringBuffer (); @@ -236,6 +226,13 @@ public class HexFormatter return hex.toString (); } + public static String getBitString (byte b) + { + String s = "0000000" + Integer.toBinaryString (b & 0xFF); + s = s.replaceAll ("0", "."); + return s.substring (s.length () - 8); + } + public static char byteValue (byte b) { int c = b & 0xFF;