001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Aleksei V. Ivaschenko
019: * @version $Revision: 1.3 $
020: */package org.apache.harmony.x.print;
021:
022: import java.io.BufferedReader;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.InputStreamReader;
026: import java.util.ArrayList;
027:
028: import javax.print.DocFlavor;
029: import javax.print.PrintException;
030: import javax.print.attribute.PrintRequestAttributeSet;
031: import javax.print.attribute.standard.MediaSize;
032: import javax.print.attribute.standard.MediaSizeName;
033: import javax.print.attribute.standard.OrientationRequested;
034:
035: /*
036: * This class implements PostScript interpreter, which
037: * converts PostScript instructions to GDI function calls.
038: * Renders to specified printer's device context.
039: */
040: public class PSInterpreter {
041:
042: private GDIClient client = null;
043: private String service = null;
044: private int serviceID = -1;
045: private PrintRequestAttributeSet attributes = null;
046:
047: private BufferedReader source = null;
048: private ArrayList queue = new ArrayList();
049: private String currentLine = null;
050: private int lineIndex = 0;
051: private int searchingIndex = 0;
052:
053: private int translateX = 0, translateY = 0;
054: private int scaleWidth = -1, scaleHeight = -1;
055: private double scaleX = 1.0, scaleY = 1.0;
056: private boolean pathOpened = false;
057: private boolean closedPathExists = false;
058: private int logWidth = 0;
059: private int logHeight = 0;
060:
061: private static final int COMMAND_MOVETO = 0;
062: private static final int COMMAND_LINETO = 1;
063: private static final int COMMAND_SETRGBCOLOR = 2;
064: private static final int COMMAND_STROKE = 3;
065: private static final int COMMAND_COLORIMAGE = 4;
066: private static final int COMMAND_TRANSLATE = 5;
067: private static final int COMMAND_SCALE = 6;
068: private static final int COMMAND_ARC = 7;
069: private static final int COMMAND_SHOW = 8;
070: private static final int COMMAND_NEWPATH = 9;
071: private static final int COMMAND_CLOSEPATH = 10;
072: private static final int COMMAND_FILL = 11;
073: private static final int COMMAND_FINDFONT = 12;
074: private static final int COMMAND_SCALEFONT = 13;
075: private static final int COMMAND_SETFONT = 14;
076: private static final int COMMAND_ROTATE = 15;
077: private static final int COMMAND_CLIP = 16;
078:
079: private static final String hexLetters = "0123456789abcdef";
080:
081: private static final String[] commands = { "moveto", "lineto",
082: "setrgbcolor", "stroke", "colorimage", "translate",
083: "scale", "arc", "show", "newpath", "closepath", "fill",
084: "findfont", "scalefont", "setfont", "rotate", "clip" };
085:
086: private static final int[] commandsParams = { 2, 2, 3, 0, 7, 2, 2,
087: 5, 1, 0, 0, 0, 1, 1, 1, 0, 0 };
088:
089: /*
090: * Constructs new PSInterpreter instance.
091: */
092: public PSInterpreter(InputStream source, String service,
093: GDIClient client, PrintRequestAttributeSet attributes) {
094: this .source = new BufferedReader(new InputStreamReader(source));
095: this .service = service;
096: this .client = client;
097: this .attributes = attributes;
098: if (service != null) {
099: getPaperDimensions(attributes);
100: serviceID = obtainServiceID(service, logWidth, logHeight);
101: }
102: }
103:
104: /*
105: * Sets up print service. Further rendering is made
106: * to new print service's device context.
107: */
108: public void setPrintService(String service) {
109: this .service = service;
110: if (serviceID >= 0) {
111: releaseServiceID(serviceID);
112: serviceID = -1;
113: }
114: if (service != null) {
115: getPaperDimensions(attributes);
116: serviceID = obtainServiceID(service, logWidth, logHeight);
117: }
118: }
119:
120: /*
121: * Starts parsing of source postscript and rendering
122: * to print service's device context.
123: */
124: public void interpret() throws PrintException {
125: if (serviceID < 0) {
126: throw new PrintException(
127: "Unrecoverable internal error in GDI client.");
128: }
129: try {
130: currentLine = source.readLine();
131: while (currentLine != null) {
132: if (currentLine.startsWith("%%EOF")) {
133: // /*
134: // * Code for debug. Prints additional page.
135: // */
136: // startPage(serviceID);
137: // setRGBColor(serviceID, 0.5, 0.5, 0.5);
138: // moveTo(serviceID, 50, 50);
139: // drawText(serviceID, "TEXT TEXT TEXT");
140: // endPage(serviceID);
141: if (!endDocument(serviceID)) {
142: releaseServiceID(serviceID);
143: serviceID = -1;
144: throw new PrintException(
145: "Unable to finish document printing.");
146: }
147: releaseServiceID(serviceID);
148: serviceID = -1;
149: return;
150: } else if (currentLine.startsWith("%%")) {
151: interpretComment();
152: } else if (currentLine.startsWith("%")
153: || currentLine.startsWith("/")) {
154: // Nothing to do - simple comment.
155: } else {
156: String lexem = getNextLexem();
157: while (lexem != null) {
158: // System.out.println("Lexem: " + lexem);
159: queue.add(lexem);
160: for (int i = 0; i < commands.length; i++) {
161: if (lexem.equals(commands[i])) {
162: interpretCommand(i);
163: }
164: }
165: lexem = getNextLexem();
166: }
167: }
168: currentLine = source.readLine();
169: lineIndex = 0;
170: }
171: endDocument(serviceID);
172: } catch (IOException ioe) {
173: throw new PrintException(
174: "Unrecoverable internal error in GDI client.");
175: }
176: }
177:
178: private String getNextLexem() throws IOException {
179: String lexem = getNextLexem(currentLine, lineIndex);
180: if (lexem.endsWith(")") || lexem.endsWith("]")) {
181: currentLine = source.readLine();
182: lineIndex = 0;
183: String prefix = (lexem.endsWith(")")) ? "( " : "[ ";
184: String endOfLexem = getNextLexem(prefix + currentLine,
185: lineIndex);
186: if (lexem.length() == 1) {
187: lexem = endOfLexem;
188: } else {
189: lexem = lexem.substring(0, lexem.length() - 1)
190: + endOfLexem;
191: }
192: lineIndex = searchingIndex - 2;
193: } else if (lexem.equals("}")) {
194: while (lexem.equals("}")) {
195: currentLine = source.readLine();
196: lineIndex = 0;
197: lexem = getNextLexem("{" + currentLine, lineIndex);
198: lineIndex = searchingIndex;
199: }
200: } else {
201: lineIndex = searchingIndex;
202: if (lexem.equals("") && lineIndex >= currentLine.length()) {
203: return null;
204: }
205: }
206: return lexem;
207: }
208:
209: private String getNextLexem(String line, int index) {
210: String lexem = "";
211: if (index < line.length()) {
212: String character = line.substring(index++, index);
213: while (index < line.length() && character.equals(" ")) {
214: character = line.substring(index++, index);
215: }
216: if (!character.equals(" ")) {
217: if (character.equals("%")) {
218: searchingIndex = line.length();
219: return lexem;
220: } else if (character.equals("(")) {
221: if (index >= line.length()) {
222: searchingIndex = index;
223: return ")";
224: }
225: character = line.substring(index++, index);
226: while (!character.equals(")")) {
227: lexem += character;
228: if (index < line.length()) {
229: character = line.substring(index++, index);
230: } else {
231: lexem += ")";
232: break;
233: }
234: }
235: } else if (character.equals("[")) {
236: if (index >= line.length()) {
237: searchingIndex = index;
238: return "]";
239: }
240: character = line.substring(index++, index);
241: while (!character.equals("]")) {
242: lexem += character;
243: if (index < line.length()) {
244: character = line.substring(index++, index);
245: } else {
246: lexem += "]";
247: break;
248: }
249: }
250: } else if (character.equals("{")) {
251: if (index >= line.length()) {
252: searchingIndex = index;
253: return "}";
254: }
255: character = line.substring(index++, index);
256: while (!character.equals("}")) {
257: if (index < line.length()) {
258: character = line.substring(index++, index);
259: } else {
260: lexem = "}";
261: break;
262: }
263: }
264: } else {
265: if (index >= line.length()) {
266: searchingIndex = index;
267: return character;
268: }
269: while (!character.equals(" ")) {
270: lexem += character;
271: if (index < line.length()) {
272: character = line.substring(index++, index);
273: } else {
274: break;
275: }
276: }
277: }
278: }
279: }
280: searchingIndex = index;
281: return lexem;
282: }
283:
284: private String getNextHexLetter() throws IOException {
285: String hex = null;
286: if (lineIndex < currentLine.length()) {
287: String character = currentLine.substring(lineIndex++,
288: lineIndex).toLowerCase();
289: while (hexLetters.indexOf(character) < 0) {
290: if (lineIndex < currentLine.length()) {
291: character = currentLine.substring(lineIndex++,
292: lineIndex).toLowerCase();
293: } else {
294: currentLine = source.readLine();
295: lineIndex = 0;
296: return getNextHexLetter();
297: }
298: }
299: hex = character;
300: } else {
301: currentLine = source.readLine();
302: lineIndex = 0;
303: return getNextHexLetter();
304: }
305: return hex;
306: }
307:
308: private String getNextHex() throws IOException {
309: return getNextHexLetter() + getNextHexLetter();
310: }
311:
312: private int hex2decimal(String hex) {
313: int multiplier = 1;
314: int decimal = 0;
315: for (int i = hex.length() - 1; i >= 0; i--) {
316: decimal += hexLetters.indexOf(hex.substring(i, i + 1))
317: * multiplier;
318: multiplier *= 16;
319: }
320: return decimal;
321: }
322:
323: private void interpretComment() throws PrintException {
324: if (currentLine.startsWith("%%Page:")) {
325: try {
326: String pageName = getNextLexem(currentLine, 7);
327: String pageNumber = getNextLexem(currentLine,
328: searchingIndex);
329: int number = Integer.parseInt(pageNumber);
330: if (!startPage(serviceID)) {
331: endDocument(serviceID);
332: throw new PrintException(
333: "Unable to start page printing.");
334: }
335: } catch (NumberFormatException nfe) {
336: System.out.println("NumberFormatException occured: "
337: + nfe);
338: nfe.printStackTrace(System.out);
339: }
340: } else if (currentLine.startsWith("%%EndPage:")) {
341: try {
342: String pageName = getNextLexem(currentLine, 10);
343: String pageNumber = getNextLexem(currentLine,
344: searchingIndex);
345: int number = Integer.parseInt(pageNumber);
346: if (pathOpened) {
347: closePath(serviceID);
348: pathOpened = false;
349: }
350: endPage(serviceID);
351: } catch (NumberFormatException nfe) {
352: System.out.println("NumberFormatException occured: "
353: + nfe);
354: nfe.printStackTrace(System.out);
355: }
356: } else if (currentLine.startsWith("%%EndSetup")
357: || currentLine.startsWith("%%EndComments")) {
358: if (!startDocument(service, serviceID, client
359: .convertAttributes(attributes,
360: new DocFlavor.INPUT_STREAM(
361: "INTERNAL/postscript")), client
362: .getJobName(attributes), client
363: .getDestination(attributes))) {
364: throw new PrintException(
365: "Unable to start document printing.");
366: }
367: }
368: }
369:
370: private void interpretCommand(int command) throws IOException {
371: if (queue.size() < commandsParams[command] + 1) {
372: System.out.println("Not enough parameters for PS command "
373: + commands[command]);
374: return;
375: }
376: queue.remove(queue.size() - 1);
377: switch (command) {
378: case COMMAND_MOVETO:
379: try {
380: double x, y;
381: y = Double.parseDouble(extractQueueLast());
382: x = Double.parseDouble(extractQueueLast());
383: if (!pathOpened) {
384: beginPath(serviceID);
385: pathOpened = true;
386: }
387: moveTo(serviceID, x, y);
388: } catch (NumberFormatException nfe) {
389: System.out.println("NumberFormatException occured: "
390: + nfe);
391: nfe.printStackTrace(System.out);
392: }
393: break;
394: case COMMAND_LINETO:
395: try {
396: double x, y;
397: y = Double.parseDouble(extractQueueLast());
398: x = Double.parseDouble(extractQueueLast());
399: if (!pathOpened) {
400: beginPath(serviceID);
401: pathOpened = true;
402: }
403: lineTo(serviceID, x, y);
404: } catch (NumberFormatException nfe) {
405: System.out.println("NumberFormatException occured: "
406: + nfe);
407: nfe.printStackTrace(System.out);
408: }
409: break;
410: case COMMAND_SETRGBCOLOR:
411: try {
412: double r, g, b;
413: b = Double.parseDouble(extractQueueLast());
414: g = Double.parseDouble(extractQueueLast());
415: r = Double.parseDouble(extractQueueLast());
416: setRGBColor(serviceID, r, g, b);
417: } catch (NumberFormatException nfe) {
418: System.out.println("NumberFormatException occured: "
419: + nfe);
420: nfe.printStackTrace(System.out);
421: }
422: break;
423: case COMMAND_NEWPATH:
424: beginPath(serviceID);
425: pathOpened = true;
426: break;
427: case COMMAND_CLOSEPATH:
428: if (pathOpened) {
429: closePath(serviceID);
430: pathOpened = false;
431: closedPathExists = true;
432: }
433: break;
434: case COMMAND_STROKE:
435: if (pathOpened) {
436: closePath(serviceID);
437: strokePath(serviceID);
438: pathOpened = false;
439: } else if (closedPathExists) {
440: strokePath(serviceID);
441: closedPathExists = false;
442: }
443: break;
444: case COMMAND_FILL:
445: if (pathOpened) {
446: closePath(serviceID);
447: fillPath(serviceID);
448: pathOpened = false;
449: } else if (closedPathExists) {
450: fillPath(serviceID);
451: closedPathExists = false;
452: }
453: break;
454: case COMMAND_COLORIMAGE:
455: if (extractQueueLast().equals("3")
456: && extractQueueLast().equals("false")) {
457: try {
458: extractQueueLast(); // removing reading procedure
459: String imageParams = extractQueueLast();
460: int depth = Integer.parseInt(extractQueueLast());
461: int height = Integer.parseInt(extractQueueLast());
462: int width = Integer.parseInt(extractQueueLast());
463: if (depth == 8) {
464: int[] imageData = new int[width * height];
465: for (int j = 0; j < height; j++) {
466: for (int i = 0; i < width; i++) {
467: int color = hex2decimal(getNextHex()
468: + getNextHex() + getNextHex());
469: imageData[j * width + i] = color;
470: }
471: }
472: if (scaleWidth < 0 || scaleHeight < 0) {
473: drawImage(serviceID, translateX,
474: translateY, width * scaleX, height
475: * scaleY, imageData, width,
476: height);
477: } else {
478: drawImage(serviceID, translateX,
479: translateY, scaleWidth,
480: scaleHeight, imageData, width,
481: height);
482: }
483: }
484: } catch (NumberFormatException nfe) {
485: System.out
486: .println("NumberFormatException occured: "
487: + nfe);
488: nfe.printStackTrace(System.out);
489: }
490: }
491: break;
492: case COMMAND_TRANSLATE:
493: try {
494: translateY = Integer.parseInt(extractQueueLast());
495: translateX = Integer.parseInt(extractQueueLast());
496: } catch (NumberFormatException nfe) {
497: System.out.println("NumberFormatException occured: "
498: + nfe);
499: nfe.printStackTrace(System.out);
500: }
501: break;
502: case COMMAND_SCALE:
503: try {
504: scaleY = Double.parseDouble(extractQueueLast());
505: scaleX = Double.parseDouble(extractQueueLast());
506: } catch (NumberFormatException nfe) {
507: System.out.println("NumberFormatException occured: "
508: + nfe);
509: nfe.printStackTrace(System.out);
510: }
511: break;
512: case COMMAND_ARC:
513: try {
514: double x, y, r, a, b;
515: b = Double.parseDouble(extractQueueLast());
516: a = Double.parseDouble(extractQueueLast());
517: r = Double.parseDouble(extractQueueLast());
518: y = Double.parseDouble(extractQueueLast());
519: x = Double.parseDouble(extractQueueLast());
520: if (!pathOpened) {
521: beginPath(serviceID);
522: pathOpened = true;
523: }
524: drawArc(serviceID, x, y, r, a, b);
525: } catch (NumberFormatException nfe) {
526: System.out.println("NumberFormatException occured: "
527: + nfe);
528: nfe.printStackTrace(System.out);
529: }
530: break;
531: case COMMAND_SHOW:
532: String text = extractQueueLast();
533: drawText(serviceID, text);
534: break;
535: default:
536: }
537: }
538:
539: private String extractQueueLast() {
540: String lexem = (String) queue.get(queue.size() - 1);
541: queue.remove(queue.size() - 1);
542: return lexem;
543: }
544:
545: private void getPaperDimensions(PrintRequestAttributeSet attrs) {
546: MediaSize size = null;
547: if (attrs != null) {
548: if (attrs.containsKey(MediaSize.class)) {
549: size = (MediaSize) attrs.get(MediaSize.class);
550: } else if (attrs.containsKey(MediaSizeName.class)) {
551: MediaSizeName name = (MediaSizeName) attrs
552: .get(MediaSizeName.class);
553: size = MediaSize.getMediaSizeForName(name);
554: } else {
555: size = MediaSize
556: .getMediaSizeForName(MediaSizeName.ISO_A4);
557: }
558: } else {
559: size = MediaSize.getMediaSizeForName(MediaSizeName.ISO_A4);
560: }
561: logWidth = (int) (size.getX(MediaSize.INCH) * 72.0);
562: logHeight = (int) (size.getY(MediaSize.INCH) * 72.0);
563: if (attributes != null) {
564: if (attributes
565: .containsValue(OrientationRequested.LANDSCAPE)) {
566: int temp = logWidth;
567: logWidth = logHeight;
568: logHeight = temp;
569: }
570: }
571: }
572:
573: private synchronized static native int obtainServiceID(String name,
574: int width, int height);
575:
576: private synchronized static native void releaseServiceID(
577: int serviceID);
578:
579: private static native boolean startDocument(String name,
580: int serviceID, int[] attributes, String jobName,
581: String destination);
582:
583: private static native boolean startPage(int serviceID);
584:
585: private static native boolean endPage(int serviceID);
586:
587: private static native boolean endDocument(int serviceID);
588:
589: private static native boolean setRGBColor(int serviceID,
590: double red, double green, double blue);
591:
592: private static native boolean moveTo(int serviceID, double x,
593: double y);
594:
595: private static native boolean lineTo(int serviceID, double x,
596: double y);
597:
598: private static native boolean drawArc(int serviceID, double x,
599: double y, double r, double a, double b);
600:
601: private static native boolean drawText(int serviceID, String text);
602:
603: private static native boolean drawImage(int serviceID, double x,
604: double y, double scalew, double scaleh, int[] image, int w,
605: int h);
606:
607: private static native boolean beginPath(int serviceID);
608:
609: private static native boolean closePath(int serviceID);
610:
611: private static native boolean strokePath(int serviceID);
612:
613: private static native boolean fillPath(int serviceID);
614:
615: private static native boolean clipPath(int serviceID);
616:
617: private static native boolean rotate(int serviceID, double alpha);
618:
619: private static native boolean setFont(int serviceID, String font);
620: }
|