comparison mupdf-source/thirdparty/tesseract/java/com/google/scrollview/ScrollView.java @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright 2007 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); You may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
6 // applicable law or agreed to in writing, software distributed under the
7 // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
8 // OF ANY KIND, either express or implied. See the License for the specific
9 // language governing permissions and limitations under the License.
10
11 package com.google.scrollview;
12
13 import com.google.scrollview.events.SVEvent;
14 import com.google.scrollview.ui.SVImageHandler;
15 import com.google.scrollview.ui.SVWindow;
16 import org.piccolo2d.nodes.PImage;
17
18 import java.io.BufferedReader;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.PrintStream;
22 import java.net.ServerSocket;
23 import java.net.Socket;
24 import java.util.ArrayList;
25 import java.util.regex.Pattern;
26
27 /**
28 * The ScrollView class is the main class which gets started from the command
29 * line. It sets up LUA and handles the network processing.
30 * @author wanke@google.com
31 */
32 public class ScrollView {
33
34 /** The port our server listens at. */
35 public static int SERVER_PORT = 8461;
36
37 /**
38 * All SVWindow objects share the same connection stream. The socket is needed
39 * to detect when the connection got closed, in/out are used to send and
40 * receive messages.
41 */
42 private static Socket socket;
43 private static PrintStream out;
44 public static BufferedReader in;
45 public static float polylineXCoords[]; // The coords being received.
46 public static float polylineYCoords[]; // The coords being received.
47 public static int polylineSize; // The size of the coords arrays.
48 public static int polylineScanned; // The size read so far.
49 private static ArrayList<SVWindow> windows; // The id to SVWindow map.
50 private static Pattern intPattern; // For checking integer arguments.
51 private static Pattern floatPattern; // For checking float arguments.
52
53 /** Keeps track of the number of messages received. */
54 static int nrInputLines = 0;
55
56 /** Prints all received messages to the console if true. */
57 static boolean debugViewNetworkTraffic = false;
58
59 /** Add a new message to the outgoing queue. */
60 public static void addMessage(SVEvent e) {
61 if (debugViewNetworkTraffic) {
62 System.out.println("(S->c) " + e.toString());
63 }
64 String str = e.toString();
65 // Send the whole thing as UTF8.
66 try {
67 byte [] utf8 = str.getBytes("UTF8");
68 out.write(utf8, 0, utf8.length);
69 } catch (java.io.UnsupportedEncodingException ex) {
70 System.out.println("Oops... can't encode to UTF8... Exiting");
71 System.exit(0);
72 }
73 out.println();
74 // Flush the output and check for errors.
75 boolean error = out.checkError();
76 if (error) {
77 System.out.println("Connection error. Quitting ScrollView Server...");
78 System.exit(0);
79 }
80 }
81
82 /** Read one message from client (assuming there are any). */
83 public static String receiveMessage() throws IOException {
84 return in.readLine();
85 }
86
87 /**
88 * The main program loop. Basically loops through receiving messages and
89 * processing them and then sending messages (if there are any).
90 */
91 private static void IOLoop() {
92 String inputLine;
93
94 try {
95 while (!socket.isClosed() && !socket.isInputShutdown() &&
96 !socket.isOutputShutdown() &&
97 socket.isConnected() && socket.isBound()) {
98 inputLine = receiveMessage();
99 if (inputLine == null) {
100 // End of stream reached.
101 break;
102 }
103 nrInputLines++;
104 if (debugViewNetworkTraffic) {
105 System.out.println("(c->S," + nrInputLines + ")" + inputLine);
106 }
107
108 if (polylineSize > polylineScanned) {
109 // We are processing a polyline.
110 // Read pairs of coordinates separated by commas.
111 boolean first = true;
112 for (String coordStr : inputLine.split(",")) {
113 int coord = Integer.parseInt(coordStr);
114 if (first) {
115 polylineXCoords[polylineScanned] = coord;
116 } else {
117 polylineYCoords[polylineScanned++] = coord;
118 }
119 first = !first;
120 }
121 assert first;
122 } else {
123 // Process this normally.
124 processInput(inputLine);
125 }
126 }
127 }
128 // Some connection error
129 catch (IOException e) {
130 System.out.println("Connection error. Quitting ScrollView Server...");
131 }
132 System.exit(0);
133 }
134
135 // Parse a comma-separated list of arguments into ArrayLists of the
136 // possible types. Each type is stored in order, but the order
137 // distinction between types is lost.
138 // Note that the format is highly constrained to what the client used
139 // to send to LUA:
140 // Quoted string -> String.
141 // true or false -> Boolean.
142 // %f format number -> Float (no %e allowed)
143 // Sequence of digits -> Integer
144 // Nothing else allowed.
145 private static void parseArguments(String argList,
146 ArrayList<Integer> intList,
147 ArrayList<Float> floatList,
148 ArrayList<String> stringList,
149 ArrayList<Boolean> boolList) {
150 // str is only non-null if an argument starts with a single or double
151 // quote. str is set back to null on completion of the string with a
152 // matching quote. If the string contains a comma then str will stay
153 // non-null across multiple argStr values until a matching closing quote.
154 // Backslash escaped quotes do not count as terminating the string.
155 String str = null;
156 for (String argStr : argList.split(",")) {
157 if (str != null) {
158 // Last string was incomplete. Append argStr to it and restore comma.
159 // Execute str += "," + argStr in Java.
160 int length = str.length() + 1 + argStr.length();
161 StringBuilder appended = new StringBuilder(length);
162 appended.append(str);
163 appended.append(",");
164 appended.append(argStr);
165 str = appended.toString();
166 } else if (argStr.length() == 0) {
167 continue;
168 } else {
169 char quote = argStr.charAt(0);
170 // If it begins with a quote then it is a string, but may not
171 // end this time if it contained a comma.
172 if (quote == '\'' || quote == '"') {
173 str = argStr;
174 }
175 }
176 if (str != null) {
177 // It began with a quote. Check that it still does.
178 assert str.charAt(0) == '\'' || str.charAt(0) == '"';
179 int len = str.length();
180 if (len > 1 && str.charAt(len - 1) == str.charAt(0)) {
181 // We have an ending quote of the right type. Now check that
182 // it is not escaped. Must have an even number of slashes before.
183 int slash = len - 1;
184 while (slash > 0 && str.charAt(slash - 1) == '\\')
185 --slash;
186 if ((len - 1 - slash) % 2 == 0) {
187 // It is now complete. Chop off the quotes and save.
188 // TODO(rays) remove the first backslash of each pair.
189 stringList.add(str.substring(1, len - 1));
190 str = null;
191 }
192 }
193 // If str is not null here, then we have a string with a comma in it.
194 // Append, and the next argument at the next iteration, but check
195 // that str is null after the loop terminates in case it was an
196 // unterminated string.
197 } else if (floatPattern.matcher(argStr).matches()) {
198 // It is a float.
199 floatList.add(Float.parseFloat(argStr));
200 } else if (argStr.equals("true")) {
201 boolList.add(true);
202 } else if (argStr.equals("false")) {
203 boolList.add(false);
204 } else if (intPattern.matcher(argStr).matches()) {
205 // Only contains digits so must be an int.
206 intList.add(Integer.parseInt(argStr));
207 }
208 // else ignore all incompatible arguments for forward compatibility.
209 }
210 // All strings must have been terminated.
211 assert str == null;
212 }
213
214 /** Executes the LUA command parsed as parameter. */
215 private static void processInput(String inputLine) {
216 if (inputLine == null) {
217 return;
218 }
219 // Execute a function encoded as a LUA statement! Yuk!
220 if (inputLine.charAt(0) == 'w') {
221 // This is a method call on a window. Parse it.
222 String noWLine = inputLine.substring(1);
223 String[] idStrs = noWLine.split("[ :]", 2);
224 int windowID = Integer.parseInt(idStrs[0]);
225 // Find the parentheses.
226 int start = inputLine.indexOf('(');
227 int end = inputLine.lastIndexOf(')');
228 // Parse the args.
229 ArrayList<Integer> intList = new ArrayList<Integer>(4);
230 ArrayList<Float> floatList = new ArrayList<Float>(2);
231 ArrayList<String> stringList = new ArrayList<String>(4);
232 ArrayList<Boolean> boolList = new ArrayList<Boolean>(3);
233 parseArguments(inputLine.substring(start + 1, end),
234 intList, floatList, stringList, boolList);
235 int colon = inputLine.indexOf(':');
236 if (colon > 1 && colon < start) {
237 // This is a regular function call. Look for the name and call it.
238 String func = inputLine.substring(colon + 1, start);
239 if (func.equals("drawLine")) {
240 windows.get(windowID).drawLine(intList.get(0), intList.get(1),
241 intList.get(2), intList.get(3));
242 } else if (func.equals("createPolyline")) {
243 windows.get(windowID).createPolyline(intList.get(0));
244 } else if (func.equals("drawPolyline")) {
245 windows.get(windowID).drawPolyline();
246 } else if (func.equals("drawRectangle")) {
247 windows.get(windowID).drawRectangle(intList.get(0), intList.get(1),
248 intList.get(2), intList.get(3));
249 } else if (func.equals("setVisible")) {
250 windows.get(windowID).setVisible(boolList.get(0));
251 } else if (func.equals("setAlwaysOnTop")) {
252 windows.get(windowID).setAlwaysOnTop(boolList.get(0));
253 } else if (func.equals("addMessage")) {
254 windows.get(windowID).addMessage(stringList.get(0));
255 } else if (func.equals("addMessageBox")) {
256 windows.get(windowID).addMessageBox();
257 } else if (func.equals("clear")) {
258 windows.get(windowID).clear();
259 } else if (func.equals("setStrokeWidth")) {
260 windows.get(windowID).setStrokeWidth(floatList.get(0));
261 } else if (func.equals("drawEllipse")) {
262 windows.get(windowID).drawEllipse(intList.get(0), intList.get(1),
263 intList.get(2), intList.get(3));
264 } else if (func.equals("pen")) {
265 if (intList.size() == 4) {
266 windows.get(windowID).pen(intList.get(0), intList.get(1),
267 intList.get(2), intList.get(3));
268 } else {
269 windows.get(windowID).pen(intList.get(0), intList.get(1),
270 intList.get(2));
271 }
272 } else if (func.equals("brush")) {
273 if (intList.size() == 4) {
274 windows.get(windowID).brush(intList.get(0), intList.get(1),
275 intList.get(2), intList.get(3));
276 } else {
277 windows.get(windowID).brush(intList.get(0), intList.get(1),
278 intList.get(2));
279 }
280 } else if (func.equals("textAttributes")) {
281 windows.get(windowID).textAttributes(stringList.get(0),
282 intList.get(0),
283 boolList.get(0),
284 boolList.get(1),
285 boolList.get(2));
286 } else if (func.equals("drawText")) {
287 windows.get(windowID).drawText(intList.get(0), intList.get(1),
288 stringList.get(0));
289 } else if (func.equals("addMenuBarItem")) {
290 if (boolList.size() > 0) {
291 windows.get(windowID).addMenuBarItem(stringList.get(0),
292 stringList.get(1),
293 intList.get(0),
294 boolList.get(0));
295 } else if (intList.size() > 0) {
296 windows.get(windowID).addMenuBarItem(stringList.get(0),
297 stringList.get(1),
298 intList.get(0));
299 } else {
300 windows.get(windowID).addMenuBarItem(stringList.get(0),
301 stringList.get(1));
302 }
303 } else if (func.equals("addPopupMenuItem")) {
304 if (stringList.size() == 4) {
305 windows.get(windowID).addPopupMenuItem(stringList.get(0),
306 stringList.get(1),
307 intList.get(0),
308 stringList.get(2),
309 stringList.get(3));
310 } else {
311 windows.get(windowID).addPopupMenuItem(stringList.get(0),
312 stringList.get(1));
313 }
314 } else if (func.equals("update")) {
315 windows.get(windowID).update();
316 } else if (func.equals("showInputDialog")) {
317 windows.get(windowID).showInputDialog(stringList.get(0));
318 } else if (func.equals("showYesNoDialog")) {
319 windows.get(windowID).showYesNoDialog(stringList.get(0));
320 } else if (func.equals("zoomRectangle")) {
321 windows.get(windowID).zoomRectangle(intList.get(0), intList.get(1),
322 intList.get(2), intList.get(3));
323 } else if (func.equals("readImage")) {
324 PImage image = SVImageHandler.readImage(intList.get(2), in);
325 windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
326 } else if (func.equals("drawImage")) {
327 PImage image = new PImage(stringList.get(0));
328 windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
329 } else if (func.equals("destroy")) {
330 windows.get(windowID).destroy();
331 }
332 // else for forward compatibility purposes, silently ignore any
333 // unrecognized function call.
334 } else {
335 // No colon. Check for create window.
336 if (idStrs[1].startsWith("= luajava.newInstance")) {
337 while (windows.size() <= windowID) {
338 windows.add(null);
339 }
340 windows.set(windowID, new SVWindow(stringList.get(1),
341 intList.get(0), intList.get(1),
342 intList.get(2), intList.get(3),
343 intList.get(4), intList.get(5),
344 intList.get(6)));
345 }
346 // else for forward compatibility purposes, silently ignore any
347 // unrecognized function call.
348 }
349 } else if (inputLine.startsWith("svmain")) {
350 // Startup or end. Startup is a lua bind, which is now a no-op.
351 if (inputLine.startsWith("svmain:exit")) {
352 exit();
353 }
354 // else for forward compatibility purposes, silently ignore any
355 // unrecognized function call.
356 }
357 // else for forward compatibility purposes, silently ignore any
358 // unrecognized function call.
359 }
360
361 /** Called from the client to make the server exit. */
362 public static void exit() {
363 System.exit(0);
364 }
365
366 /**
367 * The main function. Sets up LUA and the server connection and then calls the
368 * IOLoop.
369 */
370 public static void main(String[] args) {
371 if (args.length > 0) {
372 SERVER_PORT = Integer.parseInt(args[0]);
373 }
374 windows = new ArrayList<SVWindow>(100);
375 intPattern = Pattern.compile("[0-9-][0-9]*");
376 floatPattern = Pattern.compile("[0-9-][0-9]*\\.[0-9]*");
377
378 // Open a socket to listen on.
379 try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) {
380 System.out.println("Socket started on port " + SERVER_PORT);
381
382 // Wait (blocking) for an incoming connection
383 socket = serverSocket.accept();
384 System.out.println("Client connected");
385
386 // Setup the streams
387 out = new PrintStream(socket.getOutputStream(), true, "UTF-8");
388 in =
389 new BufferedReader(new InputStreamReader(socket.getInputStream(),
390 "UTF8"));
391 } catch (IOException e) {
392 // Something went wrong and we were unable to set up a connection. This is
393 // pretty much a fatal error.
394 // Note: The server does not get restarted automatically if this happens.
395 e.printStackTrace();
396 System.exit(1);
397 }
398
399 // Enter the main program loop.
400 IOLoop();
401 }
402 }