diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/tesseract/java/com/google/scrollview/ScrollView.java	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,402 @@
+// Copyright 2007 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); You may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
+// applicable law or agreed to in writing, software distributed under the
+// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+// OF ANY KIND, either express or implied. See the License for the specific
+// language governing permissions and limitations under the License.
+
+package com.google.scrollview;
+
+import com.google.scrollview.events.SVEvent;
+import com.google.scrollview.ui.SVImageHandler;
+import com.google.scrollview.ui.SVWindow;
+import org.piccolo2d.nodes.PImage;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+
+/**
+ * The ScrollView class is the main class which gets started from the command
+ * line. It sets up LUA and handles the network processing.
+ * @author wanke@google.com
+ */
+public class ScrollView {
+
+  /** The port our server listens at. */
+  public static int SERVER_PORT = 8461;
+
+  /**
+   * All SVWindow objects share the same connection stream. The socket is needed
+   * to detect when the connection got closed, in/out are used to send and
+   * receive messages.
+   */
+  private static Socket socket;
+  private static PrintStream out;
+  public static BufferedReader in;
+  public static float polylineXCoords[];  // The coords being received.
+  public static float polylineYCoords[];  // The coords being received.
+  public static int polylineSize;       // The size of the coords arrays.
+  public static int polylineScanned;    // The size read so far.
+  private static ArrayList<SVWindow> windows;  // The id to SVWindow map.
+  private static Pattern intPattern;        // For checking integer arguments.
+  private static Pattern floatPattern;     // For checking float arguments.
+
+  /** Keeps track of the number of messages received. */
+  static int nrInputLines = 0;
+
+  /** Prints all received messages to the console if true. */
+  static boolean debugViewNetworkTraffic = false;
+
+  /** Add a new message to the outgoing queue. */
+  public static void addMessage(SVEvent e) {
+    if (debugViewNetworkTraffic) {
+      System.out.println("(S->c) " + e.toString());
+    }
+    String str = e.toString();
+    // Send the whole thing as UTF8.
+    try {
+      byte [] utf8 = str.getBytes("UTF8");
+      out.write(utf8, 0, utf8.length);
+    } catch (java.io.UnsupportedEncodingException ex) {
+      System.out.println("Oops... can't encode to UTF8... Exiting");
+      System.exit(0);
+    }
+    out.println();
+    // Flush the output and check for errors.
+    boolean error = out.checkError();
+    if (error) {
+      System.out.println("Connection error. Quitting ScrollView Server...");
+      System.exit(0);
+    }
+  }
+
+  /** Read one message from client (assuming there are any). */
+  public static String receiveMessage() throws IOException {
+    return in.readLine();
+  }
+
+  /**
+   * The main program loop. Basically loops through receiving messages and
+   * processing them and then sending messages (if there are any).
+   */
+  private static void IOLoop() {
+    String inputLine;
+
+    try {
+      while (!socket.isClosed() && !socket.isInputShutdown() &&
+             !socket.isOutputShutdown() &&
+             socket.isConnected() && socket.isBound()) {
+        inputLine = receiveMessage();
+        if (inputLine == null) {
+          // End of stream reached.
+          break;
+        }
+        nrInputLines++;
+        if (debugViewNetworkTraffic) {
+          System.out.println("(c->S," + nrInputLines + ")" + inputLine);
+        }
+
+        if (polylineSize > polylineScanned) {
+          // We are processing a polyline.
+          // Read pairs of coordinates separated by commas.
+          boolean first = true;
+          for (String coordStr : inputLine.split(",")) {
+            int coord = Integer.parseInt(coordStr);
+            if (first) {
+              polylineXCoords[polylineScanned] = coord;
+            } else {
+              polylineYCoords[polylineScanned++] = coord;
+            }
+            first = !first;
+          }
+          assert first;
+        } else {
+          // Process this normally.
+          processInput(inputLine);
+        }
+      }
+    }
+    // Some connection error
+    catch (IOException e) {
+      System.out.println("Connection error. Quitting ScrollView Server...");
+    }
+    System.exit(0);
+  }
+
+  // Parse a comma-separated list of arguments into ArrayLists of the
+  // possible types. Each type is stored in order, but the order
+  // distinction between types is lost.
+  // Note that the format is highly constrained to what the client used
+  // to send to LUA:
+  // Quoted string -> String.
+  // true or false -> Boolean.
+  // %f format number -> Float (no %e allowed)
+  // Sequence of digits -> Integer
+  // Nothing else allowed.
+  private static void parseArguments(String argList,
+                                     ArrayList<Integer> intList,
+                                     ArrayList<Float> floatList,
+                                     ArrayList<String> stringList,
+                                     ArrayList<Boolean> boolList) {
+    // str is only non-null if an argument starts with a single or double
+    // quote. str is set back to null on completion of the string with a
+    // matching quote. If the string contains a comma then str will stay
+    // non-null across multiple argStr values until a matching closing quote.
+    // Backslash escaped quotes do not count as terminating the string.
+    String str = null;
+    for (String argStr : argList.split(",")) {
+      if (str != null) {
+        // Last string was incomplete. Append argStr to it and restore comma.
+        // Execute str += "," + argStr in Java.
+        int length = str.length() + 1 + argStr.length();
+        StringBuilder appended = new StringBuilder(length);
+        appended.append(str);
+        appended.append(",");
+        appended.append(argStr);
+        str =  appended.toString();
+      } else if (argStr.length() == 0) {
+        continue;
+      } else {
+        char quote = argStr.charAt(0);
+        // If it begins with a quote then it is a string, but may not
+        // end this time if it contained a comma.
+        if (quote == '\'' || quote == '"') {
+          str = argStr;
+        }
+      }
+      if (str != null) {
+        // It began with a quote. Check that it still does.
+        assert str.charAt(0) == '\'' || str.charAt(0) == '"';
+        int len = str.length();
+        if (len > 1 && str.charAt(len - 1) == str.charAt(0)) {
+          // We have an ending quote of the right type. Now check that
+          // it is not escaped. Must have an even number of slashes before.
+          int slash = len - 1;
+          while (slash > 0 && str.charAt(slash - 1) == '\\')
+            --slash;
+          if ((len - 1 - slash) % 2 == 0) {
+            // It is now complete. Chop off the quotes and save.
+            // TODO(rays) remove the first backslash of each pair.
+            stringList.add(str.substring(1, len - 1));
+            str = null;
+          }
+        }
+        // If str is not null here, then we have a string with a comma in it.
+        // Append, and the next argument at the next iteration, but check
+        // that str is null after the loop terminates in case it was an
+        // unterminated string.
+      } else if (floatPattern.matcher(argStr).matches()) {
+        // It is a float.
+        floatList.add(Float.parseFloat(argStr));
+      } else if (argStr.equals("true")) {
+        boolList.add(true);
+      } else if (argStr.equals("false")) {
+        boolList.add(false);
+      } else if (intPattern.matcher(argStr).matches()) {
+        // Only contains digits so must be an int.
+        intList.add(Integer.parseInt(argStr));
+      }
+      // else ignore all incompatible arguments for forward compatibility.
+    }
+    // All strings must have been terminated.
+    assert str == null;
+  }
+
+  /** Executes the LUA command parsed as parameter. */
+  private static void processInput(String inputLine) {
+    if (inputLine == null) {
+      return;
+    }
+    // Execute a function encoded as a LUA statement! Yuk!
+    if (inputLine.charAt(0) == 'w') {
+      // This is a method call on a window. Parse it.
+      String noWLine = inputLine.substring(1);
+      String[] idStrs = noWLine.split("[ :]", 2);
+      int windowID = Integer.parseInt(idStrs[0]);
+      // Find the parentheses.
+      int start = inputLine.indexOf('(');
+      int end = inputLine.lastIndexOf(')');
+      // Parse the args.
+      ArrayList<Integer> intList = new ArrayList<Integer>(4);
+      ArrayList<Float> floatList = new ArrayList<Float>(2);
+      ArrayList<String> stringList = new ArrayList<String>(4);
+      ArrayList<Boolean> boolList = new ArrayList<Boolean>(3);
+      parseArguments(inputLine.substring(start + 1, end),
+                     intList, floatList, stringList, boolList);
+      int colon = inputLine.indexOf(':');
+      if (colon > 1 && colon < start) {
+        // This is a regular function call. Look for the name and call it.
+        String func = inputLine.substring(colon + 1, start);
+        if (func.equals("drawLine")) {
+          windows.get(windowID).drawLine(intList.get(0), intList.get(1),
+                                         intList.get(2), intList.get(3));
+        } else if (func.equals("createPolyline")) {
+          windows.get(windowID).createPolyline(intList.get(0));
+        } else if (func.equals("drawPolyline")) {
+          windows.get(windowID).drawPolyline();
+        } else if (func.equals("drawRectangle")) {
+          windows.get(windowID).drawRectangle(intList.get(0), intList.get(1),
+                                              intList.get(2), intList.get(3));
+        } else if (func.equals("setVisible")) {
+          windows.get(windowID).setVisible(boolList.get(0));
+        } else if (func.equals("setAlwaysOnTop")) {
+          windows.get(windowID).setAlwaysOnTop(boolList.get(0));
+        } else if (func.equals("addMessage")) {
+          windows.get(windowID).addMessage(stringList.get(0));
+        } else if (func.equals("addMessageBox")) {
+          windows.get(windowID).addMessageBox();
+        } else if (func.equals("clear")) {
+          windows.get(windowID).clear();
+        } else if (func.equals("setStrokeWidth")) {
+          windows.get(windowID).setStrokeWidth(floatList.get(0));
+        } else if (func.equals("drawEllipse")) {
+          windows.get(windowID).drawEllipse(intList.get(0), intList.get(1),
+                                            intList.get(2), intList.get(3));
+        } else if (func.equals("pen")) {
+          if (intList.size() == 4) {
+            windows.get(windowID).pen(intList.get(0), intList.get(1),
+                                      intList.get(2), intList.get(3));
+          } else {
+            windows.get(windowID).pen(intList.get(0), intList.get(1),
+                                      intList.get(2));
+          }
+        } else if (func.equals("brush")) {
+          if (intList.size() == 4) {
+            windows.get(windowID).brush(intList.get(0), intList.get(1),
+                                        intList.get(2), intList.get(3));
+          } else {
+            windows.get(windowID).brush(intList.get(0), intList.get(1),
+                                        intList.get(2));
+          }
+        } else if (func.equals("textAttributes")) {
+          windows.get(windowID).textAttributes(stringList.get(0),
+                                               intList.get(0),
+                                               boolList.get(0),
+                                               boolList.get(1),
+                                               boolList.get(2));
+        } else if (func.equals("drawText")) {
+          windows.get(windowID).drawText(intList.get(0), intList.get(1),
+                                         stringList.get(0));
+        } else if (func.equals("addMenuBarItem")) {
+          if (boolList.size() > 0) {
+            windows.get(windowID).addMenuBarItem(stringList.get(0),
+                                                 stringList.get(1),
+                                                 intList.get(0),
+                                                 boolList.get(0));
+          } else if (intList.size() > 0) {
+            windows.get(windowID).addMenuBarItem(stringList.get(0),
+                                                 stringList.get(1),
+                                                 intList.get(0));
+          } else {
+            windows.get(windowID).addMenuBarItem(stringList.get(0),
+                                                 stringList.get(1));
+          }
+        } else if (func.equals("addPopupMenuItem")) {
+          if (stringList.size() == 4) {
+            windows.get(windowID).addPopupMenuItem(stringList.get(0),
+                                                   stringList.get(1),
+                                                   intList.get(0),
+                                                   stringList.get(2),
+                                                   stringList.get(3));
+          } else {
+             windows.get(windowID).addPopupMenuItem(stringList.get(0),
+                                                    stringList.get(1));
+          }
+        } else if (func.equals("update")) {
+          windows.get(windowID).update();
+        } else if (func.equals("showInputDialog")) {
+          windows.get(windowID).showInputDialog(stringList.get(0));
+        } else if (func.equals("showYesNoDialog")) {
+          windows.get(windowID).showYesNoDialog(stringList.get(0));
+        } else if (func.equals("zoomRectangle")) {
+          windows.get(windowID).zoomRectangle(intList.get(0), intList.get(1),
+                                              intList.get(2), intList.get(3));
+        } else if (func.equals("readImage")) {
+          PImage image = SVImageHandler.readImage(intList.get(2), in);
+          windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
+        } else if (func.equals("drawImage")) {
+          PImage image = new PImage(stringList.get(0));
+          windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
+        } else if (func.equals("destroy")) {
+          windows.get(windowID).destroy();
+        }
+        // else for forward compatibility purposes, silently ignore any
+        // unrecognized function call.
+      } else {
+        // No colon. Check for create window.
+        if (idStrs[1].startsWith("= luajava.newInstance")) {
+          while (windows.size() <= windowID) {
+            windows.add(null);
+          }
+          windows.set(windowID, new SVWindow(stringList.get(1),
+                                             intList.get(0), intList.get(1),
+                                             intList.get(2), intList.get(3),
+                                             intList.get(4), intList.get(5),
+                                             intList.get(6)));
+        }
+        // else for forward compatibility purposes, silently ignore any
+        // unrecognized function call.
+      }
+    } else if (inputLine.startsWith("svmain")) {
+        // Startup or end. Startup is a lua bind, which is now a no-op.
+        if (inputLine.startsWith("svmain:exit")) {
+          exit();
+        }
+        // else for forward compatibility purposes, silently ignore any
+        // unrecognized function call.
+    }
+    // else for forward compatibility purposes, silently ignore any
+    // unrecognized function call.
+  }
+
+  /** Called from the client to make the server exit. */
+  public static void exit() {
+    System.exit(0);
+  }
+
+  /**
+   * The main function. Sets up LUA and the server connection and then calls the
+   * IOLoop.
+   */
+  public static void main(String[] args) {
+    if (args.length > 0) {
+      SERVER_PORT = Integer.parseInt(args[0]);
+    }
+    windows = new ArrayList<SVWindow>(100);
+    intPattern = Pattern.compile("[0-9-][0-9]*");
+    floatPattern = Pattern.compile("[0-9-][0-9]*\\.[0-9]*");
+
+    // Open a socket to listen on.
+    try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) {
+      System.out.println("Socket started on port " + SERVER_PORT);
+
+      // Wait (blocking) for an incoming connection
+      socket = serverSocket.accept();
+      System.out.println("Client connected");
+
+      // Setup the streams
+      out = new PrintStream(socket.getOutputStream(), true, "UTF-8");
+      in =
+          new BufferedReader(new InputStreamReader(socket.getInputStream(),
+              "UTF8"));
+    } catch (IOException e) {
+      // Something went wrong and we were unable to set up a connection. This is
+      // pretty much a fatal error.
+      // Note: The server does not get restarted automatically if this happens.
+      e.printStackTrace();
+      System.exit(1);
+    }
+
+    // Enter the main program loop.
+    IOLoop();
+  }
+}