001: /*
002: * $Id: FontToy.java,v 1.3 2007/12/20 18:33:32 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package test;
023:
024: /*
025: * FontToy.java
026: *
027: * Created on September 15, 2003, 7:38 AM
028: */
029: import java.awt.Graphics;
030: import java.awt.Graphics2D;
031: import java.awt.Dimension;
032: import java.awt.BorderLayout;
033: import java.awt.Color;
034: import java.awt.Image;
035: import java.awt.Font;
036:
037: import java.awt.event.*;
038: import java.awt.geom.*;
039:
040: import java.io.*;
041:
042: import java.util.*;
043:
044: import java.nio.ByteBuffer;
045: import java.nio.channels.FileChannel;
046:
047: import javax.swing.*;
048: import javax.swing.event.*;
049:
050: import com.sun.pdfview.*;
051: import com.sun.pdfview.font.*;
052:
053: /**
054: *
055: */
056: public class FontToy extends JPanel {
057: /** the panel to draw to */
058: private static FontToy toy;
059:
060: /** the list of glyphs in the current font */
061: private static JComboBox glyphBox;
062:
063: /** the fonts */
064: private Set fonts;
065:
066: /** the current font */
067: private PDFFont font;
068:
069: /** the current glyph */
070: private PDFGlyph glyph;
071:
072: /** the font to draw glyphs in */
073: private Font gfont;
074:
075: /** Creates a new instance of FontToy */
076: public FontToy(PDFFile pdf) throws IOException {
077: PDFObject root = pdf.getRoot();
078:
079: PDFObject pagesObj = (PDFObject) root.getDictRef("Pages");
080: fonts = findFonts(pagesObj, new HashMap());
081:
082: gfont = new Font("Sans-serif", Font.PLAIN, 10);
083: }
084:
085: /** get the set of associated fonts */
086: public Set getFonts() {
087: return fonts;
088: }
089:
090: /** get the current font */
091: public PDFFont getCurrentFont() {
092: return font;
093: }
094:
095: /** set the current font */
096: public void setCurrentFont(PDFFont font) {
097: this .font = font;
098: }
099:
100: /** get the current glyph */
101: public PDFGlyph getCurrentGlyph() {
102: return glyph;
103: }
104:
105: /** set the current glyph */
106: public void setCurrentGlyph(PDFGlyph glyph) {
107: this .glyph = glyph;
108: }
109:
110: /**
111: * paint the current glyph on the screen
112: */
113: public void paint(Graphics g) {
114: // System.out.println("Repaint!");
115:
116: Graphics2D g2 = (Graphics2D) g;
117:
118: int width = getWidth();
119: int height = getHeight();
120:
121: g2.setColor(Color.WHITE);
122: g2.fillRect(0, 0, width, height);
123:
124: g2.setColor(Color.BLACK);
125:
126: if (glyph == null) {
127: return;
128: }
129:
130: GeneralPath gp = glyph.getShape();
131: PDFPage page = glyph.getPage();
132: if (gp != null) {
133: drawShape(g2, gp, width, height);
134: } else if (page != null) {
135: drawPage(g2, page, width, height);
136: }
137: }
138:
139: /**
140: * Draw a shpae in fancy outline form
141: */
142: private void drawShape(Graphics2D g2, GeneralPath gp, int w, int h) {
143: // System.out.println("Drawing shape in: " + w + " x " + h);
144:
145: float curX = 0;
146: float curY = 0;
147: float startX = 0;
148: float startY = 0;
149:
150: Rectangle2D border = gp.getBounds2D();
151:
152: double scaleX = (w - 20) / border.getWidth();
153: double scaleY = (h - 20) / border.getHeight();
154:
155: if (scaleX < scaleY) {
156: scaleY = scaleX;
157: } else {
158: scaleX = scaleY;
159: }
160:
161: double transX = 10 - (border.getX() * scaleX);
162: double transY = h - 10 + (border.getY() * scaleY);
163:
164: AffineTransform at = new AffineTransform(scaleX, 0, 0, -scaleY,
165: transX, transY);
166:
167: Rectangle2D borderTrans = gp.createTransformedShape(at)
168: .getBounds2D();
169:
170: g2.setColor(Color.CYAN);
171: g2.fill(gp.createTransformedShape(at));
172: g2.setColor(Color.BLACK);
173:
174: int num = 0;
175:
176: PathIterator pi = gp.getPathIterator(at);
177: while (!pi.isDone()) {
178: float[] coords = new float[6];
179:
180: switch (pi.currentSegment(coords)) {
181: case PathIterator.SEG_MOVETO:
182: curX = coords[0];
183: curY = coords[1];
184:
185: drawPoint(g2, num++, curX, curY, false);
186:
187: startX = curX;
188: startY = curY;
189: break;
190: case PathIterator.SEG_LINETO:
191: Line2D line = new Line2D.Float(curX, curY, coords[0],
192: coords[1]);
193: g2.draw(line);
194:
195: drawPoint(g2, num++, coords[0], coords[1], false);
196:
197: curX = coords[0];
198: curY = coords[1];
199: break;
200: case PathIterator.SEG_CUBICTO:
201: CubicCurve2D curve = new CubicCurve2D.Float(curX, curY,
202: coords[0], coords[1], coords[2], coords[3],
203: coords[4], coords[5]);
204: g2.draw(curve);
205:
206: drawPoint(g2, num++, coords[0], coords[1], true);
207: drawPoint(g2, num++, coords[2], coords[3], true);
208: drawPoint(g2, num++, coords[4], coords[5], false);
209:
210: curX = coords[4];
211: curY = coords[5];
212: break;
213: case PathIterator.SEG_QUADTO:
214: QuadCurve2D curveQ = new QuadCurve2D.Float(curX, curY,
215: coords[0], coords[1], coords[2], coords[3]);
216: g2.draw(curveQ);
217:
218: drawPoint(g2, num++, coords[0], coords[1], true);
219: drawPoint(g2, num++, coords[2], coords[3], false);
220:
221: curX = coords[2];
222: curY = coords[3];
223: break;
224: case PathIterator.SEG_CLOSE:
225: Line2D.Float line2 = new Line2D.Float(curX, curY,
226: startX, startY);
227:
228: g2.draw(line2);
229: curX = startX;
230: curY = startY;
231: break;
232:
233: }
234:
235: pi.next();
236: }
237: }
238:
239: /**
240: * Draw and label a point
241: */
242: public void drawPoint(Graphics2D g, int num, float x, float y,
243: boolean curvectl) {
244: GeneralPath gp = new GeneralPath();
245: if (curvectl) {
246: gp.moveTo(x - 1, y - 1);
247: gp.lineTo(x + 1, y + 1);
248: gp.moveTo(x - 1, y + 1);
249: gp.lineTo(x + 1, y - 1);
250: } else {
251: gp.moveTo(x - 1, y - 1);
252: gp.lineTo(x - 1, y + 1);
253: gp.lineTo(x + 1, y + 1);
254: gp.lineTo(x + 1, y - 1);
255: gp.closePath();
256: }
257:
258: g.setColor(Color.red);
259: g.draw(gp);
260: g.setColor(Color.blue);
261: g.setFont(gfont);
262: g.drawString(String.valueOf(num), x + 3, y + 3);
263: }
264:
265: /**
266: * Draw a page
267: */
268: private void drawPage(Graphics2D g2, PDFPage page, int w, int h) {
269: // System.out.println("Drawing page in: " + w + " x " + h);
270:
271: Dimension pageSize = page.getUnstretchedSize(w - 20, h - 20,
272: null);
273: Image image = page.getImage(pageSize.width, pageSize.height,
274: null, null, true, true);
275:
276: g2.drawImage(image, 0, 0, null);
277: }
278:
279: /**
280: * Walk the PDF Tree looking for fonts
281: *
282: * @param pagedict the top of the pages tree
283: * @param resources a HashMap that will be filled with any resource
284: * definitions encountered on the search for the page
285: */
286: private Set findFonts(PDFObject pagedict, Map resources)
287: throws IOException {
288: Set outSet = new HashSet();
289:
290: PDFObject rsrcObj = pagedict.getDictRef("Resources");
291: if (rsrcObj != null) {
292: // copy the resources so we don't overwrite them in
293: // children
294: HashMap rsrcMap = new HashMap();
295: rsrcMap.putAll(resources);
296:
297: Map rsrc = rsrcObj.getDictionary();
298: rsrcMap.putAll(rsrc);
299:
300: if (rsrc.containsKey("Font")) {
301: PDFObject fontsObj = (PDFObject) rsrc.get("Font");
302:
303: for (Iterator i = fontsObj.getDictKeys(); i.hasNext();) {
304: String key = (String) i.next();
305: PDFObject fontObj = (PDFObject) fontsObj
306: .getDictRef(key);
307:
308: try {
309: PDFFont font = PDFFont
310: .getFont(fontObj, rsrcMap);
311:
312: // System.out.println("Found font: " + font.getBaseFont());
313:
314: outSet.add(font);
315: } catch (Exception ex) {
316: // oh well
317: System.out.println("Error finding font from "
318: + fontObj);
319: ex.printStackTrace();
320: }
321: }
322: }
323:
324: // look at XObjects for fonts as well
325: if (rsrc.containsKey("XObject")) {
326: PDFObject xobjsObj = (PDFObject) rsrc.get("XObject");
327:
328: for (Iterator i = xobjsObj.getDictKeys(); i.hasNext();) {
329: String key = (String) i.next();
330: PDFObject xobj = (PDFObject) xobjsObj
331: .getDictRef(key);
332: outSet.addAll(findFonts(xobj, new HashMap()));
333: }
334: }
335:
336: resources = rsrcMap;
337: }
338:
339: PDFObject kidsObj = pagedict.getDictRef("Kids");
340: if (kidsObj != null) {
341: PDFObject[] kids = kidsObj.getArray();
342: for (int i = 0; i < kids.length; i++) {
343: outSet.addAll(findFonts(kids[i], resources));
344: }
345: }
346:
347: return outSet;
348: }
349:
350: /**
351: * Called when a key is typed
352: */
353: private static void keyPressed(KeyEvent k) {
354: int curIndex = glyphBox.getSelectedIndex();
355: int nextIndex = curIndex;
356:
357: if (k.getKeyCode() == KeyEvent.VK_LEFT) {
358: nextIndex--;
359: if (nextIndex < 0) {
360: nextIndex = glyphBox.getItemCount() - 1;
361: }
362: } else if (k.getKeyCode() == KeyEvent.VK_RIGHT) {
363: nextIndex++;
364:
365: if (nextIndex >= glyphBox.getItemCount()) {
366: nextIndex = 0;
367: }
368: }
369:
370: if (nextIndex != curIndex) {
371: glyphBox.setSelectedIndex(nextIndex);
372: }
373: }
374:
375: /**
376: * Called when a new glyph is selected
377: */
378: private static void glyphSelected(Integer glyphID) {
379: // System.out.println("Glyph " + glyphID + " selected.");
380:
381: char glyphChar = (char) (glyphID.intValue());
382: String s = String.valueOf(glyphChar);
383:
384: PDFFont font = toy.getCurrentFont();
385:
386: List l = font.getGlyphs(s);
387:
388: PDFGlyph glyph = (PDFGlyph) l.get(0);
389:
390: toy.setCurrentGlyph(glyph);
391: toy.repaint();
392: }
393:
394: /**
395: * Called when a new font is selected
396: */
397: private static void fontSelected(PDFFont font) {
398: // System.out.println("Font " + font + " selected.");
399:
400: toy.setCurrentFont(font);
401:
402: int start = 0;
403: int end = 255;
404:
405: if (font instanceof OutlineFont) {
406: start = ((OutlineFont) font).getFirstChar();
407: end = ((OutlineFont) font).getLastChar();
408: } else if (font instanceof Type3Font) {
409: start = ((Type3Font) font).getFirstChar();
410: end = ((Type3Font) font).getLastChar();
411: }
412:
413: if (start < 0) {
414: start = 0;
415: }
416:
417: if (end < 0) {
418: end = 255;
419: }
420:
421: Vector objs = new Vector(end - start + 1);
422: for (int i = start; i <= end; i++) {
423: objs.add(new Integer(i));
424: }
425:
426: glyphBox.setModel(new DefaultComboBoxModel(objs));
427:
428: glyphSelected(new Integer(start));
429: }
430:
431: /**
432: * Create the frame and stuff
433: */
434: private static void createUI() throws Exception {
435: Box controlPanel = Box.createHorizontalBox();
436:
437: glyphBox = new JComboBox();
438: glyphBox.addItemListener(new ItemListener() {
439: public void itemStateChanged(ItemEvent ie) {
440: if (ie.getStateChange() == ItemEvent.SELECTED) {
441: glyphSelected((Integer) ie.getItem());
442: }
443: }
444: });
445:
446: Object[] fontObjs = toy.getFonts().toArray();
447:
448: JComboBox comboBox = new JComboBox(fontObjs);
449: comboBox.addItemListener(new ItemListener() {
450: public void itemStateChanged(ItemEvent ie) {
451: if (ie.getStateChange() == ItemEvent.SELECTED) {
452: fontSelected((PDFFont) ie.getItem());
453: }
454: }
455: });
456: comboBox.setMaximumSize(new Dimension(200, 50));
457:
458: fontSelected((PDFFont) fontObjs[0]);
459:
460: controlPanel.add(new JLabel("Fonts:"));
461: controlPanel.add(comboBox);
462:
463: controlPanel.add(Box.createHorizontalStrut(15));
464:
465: controlPanel.add(new JLabel("Glyphs:"));
466: controlPanel.add(glyphBox);
467:
468: JPanel ftPanel = new JPanel();
469: ftPanel.setLayout(new BorderLayout());
470:
471: ftPanel.add(controlPanel, BorderLayout.NORTH);
472: ftPanel.add(toy, BorderLayout.CENTER);
473:
474: ftPanel.setFocusable(true);
475: ftPanel.requestFocus();
476: ftPanel.addKeyListener(new KeyAdapter() {
477: public void keyTyped(KeyEvent k) {
478: keyPressed(k);
479: }
480: });
481:
482: JFrame jf = new JFrame("Font Toy");
483: jf.setContentPane(ftPanel);
484:
485: jf.addWindowListener(new WindowAdapter() {
486: public void windowClosing(WindowEvent we) {
487: System.exit(-1);
488: }
489: });
490:
491: jf.setSize(640, 480);
492:
493: jf.show();
494: }
495:
496: /**
497: * @param args the command line arguments
498: */
499: public static void main(String[] args) {
500: if (args.length < 1) {
501: System.out.println("Usage:");
502: System.out.println(" FontToy <pdf-file>");
503: System.exit(-1);
504: }
505:
506: String fileName = args[0];
507:
508: try {
509: RandomAccessFile raf = new RandomAccessFile(fileName, "r");
510: FileChannel channel = raf.getChannel();
511: ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY,
512: 0, channel.size());
513:
514: PDFFile file = new PDFFile(buf);
515:
516: toy = new FontToy(file);
517: createUI();
518: } catch (Exception ex) {
519: ex.printStackTrace();
520: }
521: }
522: }
|