Gast commit!

This commit is contained in:
2026-02-10 19:01:19 +08:00
commit 345f628a8a
3074 changed files with 278037 additions and 0 deletions
@@ -0,0 +1,94 @@
package mobi.librera.smartreflow;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import javax.imageio.ImageIO;
public class AwtPlatformImage implements PlatformImage<Image> {
BufferedImage image;
WritableRaster raster;
public AwtPlatformImage() {
}
public AwtPlatformImage(PlatformImage image) {
create(image.getWidth(), image.getHeight());
}
public AwtPlatformImage(String path) throws Exception {
load(path);
}
public AwtPlatformImage(int w, int h) throws Exception {
create(w, h);
}
@Override
public void load(Image img) {
image = (BufferedImage) img;
raster = (WritableRaster) image.getData();
}
@Override
public void load(String path) throws Exception {
image = ImageIO.read(new File(path));
raster = (WritableRaster) image.getData();
}
@Override
public void create(int width, int height) {
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
raster = image.getData().createCompatibleWritableRaster();
}
@Override
public int getWidth() {
return raster.getWidth();
}
@Override
public int getHeight() {
return raster.getHeight();
}
@Override
public void setPixel(int x, int y, int[] color) {
try {
raster.setPixel(x, y, color);
}catch (Exception e){
ImageUtils.log("setPixel", raster.getHeight(), raster.getHeight(), x,y);
throw e;
}
}
@Override
public Image getImage() {
image.setData(raster);
return image;
}
@Override
public int[] getPixel(int x, int y) {
int[] pixel = new int[4];
raster.getPixel(x, y, pixel);
return pixel;
}
@Override
public int getRedPixel(int x, int y) {
int[] pixel = new int[4];
raster.getPixel(x, y, pixel);
return pixel[0];
}
@Override
public boolean isBlackPixel(int x, int y) {
return getRedPixel(x, y) < 180;
}
}
@@ -0,0 +1,216 @@
package mobi.librera.smartreflow;
import java.util.ArrayList;
import java.util.List;
import mobi.librera.smartreflow.model.Line;
import mobi.librera.smartreflow.model.Rect;
import mobi.librera.smartreflow.model.Word;
public class ImageUtils {
public static Logger platformLogger = new PrintLnLogger();
public static void setBackgroundColor(PlatformImage image, int[] color) {
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
image.setPixel(x, y, color);
}
}
}
public static void removeWhiteBegin(PlatformImage img, Rect line) {
for (int x = line.x1; x < line.x2; x++) {
for (int y = line.y1; y < line.y2; y++) {
if (ImageUtils.isBlackPixel(img, x, y)) {
line.x1 = x;
return;
}
}
}
}
public static void removeWhiteEnd(PlatformImage img, Rect line) {
for (int x = line.x2; x > line.x1; x--) {
for (int y = line.y1; y < line.y2; y++) {
if (ImageUtils.isBlackPixel(img, x, y)) {
line.x2 = x;
return;
}
}
}
}
public static boolean isBlackPixel(PlatformImage image, int x, int y) {
return image.getPixel(x, y)[0] < 180;
}
public static boolean isWhiteHorizontal(PlatformImage img, int y, int x1, int x2) {
for (int x = x1; x < x2; x += 1) {
if (ImageUtils.isBlackPixel(img, x, y)) {
return false;
}
}
return true;
}
public static boolean isWhiteVertical(PlatformImage img, int x, int y1, int y2) {
for (int y = y1; y < y2; y += 1) {
if (ImageUtils.isBlackPixel(img, x, y)) {
return false;
}
}
return true;
}
public static boolean isBlankVertical(PlatformImage img, int x, int y1, int y2, int dx) {
boolean isBlank = true;
for (int y = y1; y < y2; y += dx) {
if (img.isBlackPixel(x, y)) {
isBlank = false;
break;
}
}
return isBlank;
}
public static void drawRect(PlatformImage image, Rect r, int[] color) {
for (int i = r.x1; i < r.x2; i++) {
image.setPixel(i, r.y1, color);
image.setPixel(i, r.y2, color);
}
for (int i = r.y1; i < r.y2; i++) {
image.setPixel(r.x1, i, color);
image.setPixel(r.x2, i, color);
}
}
public static void drawWord(Rect w, int dx, int dy, PlatformImage input, PlatformImage output) {
for (int i = w.x1; i < w.x2; i++) {
for (int j = w.y1; j < w.y2; j++) {
int pixel[] = input.getPixel(i, j);
final int x = dx + (i - w.x1);
final int y = dy + (j - w.y1);
if(x<output.getWidth() && y< output.getHeight()) {
output.setPixel(x, y, pixel);
}
}
}
}
public static void drawRect(PlatformImage image, int x, int y, int x1, int y1, int[] color) {
for (int i = x; i < x1; i++) {
image.setPixel(i, y, color);
image.setPixel(i, y1, color);
}
for (int i = y; i < y1; i++) {
image.setPixel(x, i, color);
image.setPixel(x1, i, color);
}
}
public static void copyRect(PlatformImage input, PlatformImage output, int x, int y, int x1, int y1) {
for (int i = x; i < x1; i++) {
for (int j = y; j < y1; j++) {
final int[] pixel = input.getPixel(i, j);
output.setPixel(i, j, pixel);
}
}
}
public static void log(Object... strings) {
StringBuilder s = new StringBuilder();
for (Object str : strings) {
s.append(str + "|");
}
platformLogger.log(s.toString());
}
public static List<Rect> splitHorizontal(PlatformImage img, Rect rect, int minLineHeight) {
Rect line = null;
List<Rect> rects = new ArrayList<>();
boolean prevWhite = true;
for (int y = rect.y1; y < rect.y2; y += 1) {
boolean isWhite = ImageUtils.isWhiteHorizontal(img, y, rect.x1, rect.x2);
if (prevWhite && !isWhite) {
line = new Line(rect.x1, y);
}
if (isWhite && !prevWhite) {
if (y - line.y1 > minLineHeight) {
line.x2 = rect.x2;
line.y2 = y;
rects.add(line);
line = null;
}
}
prevWhite = isWhite;
}
if (line != null) {
line.x2 = rect.x2;
line.y2 = rect.y2;
rects.add(line);
}
return rects;
}
public static List<Word> splitVertical(PlatformImage img, Rect rect, int minSpaceSize) {
Word line = null;
List<Word> rects = new ArrayList<>();
boolean prevWhite = true;
int countWhite = 0;
for (int x = rect.x1; x < rect.x2; x += 1) {
boolean isWhite = ImageUtils.isWhiteVertical(img, x, rect.y1, rect.y2);
if (isWhite) {
countWhite++;
if (countWhite < minSpaceSize) {
continue;
}
}
countWhite = 0;
if (prevWhite && !isWhite) {
line = new Word(x, rect.y1);
}
if (isWhite && !prevWhite) {
line.y2 = rect.y2;
line.x2 = x;
rects.add(line);
line = null;
}
prevWhite = isWhite;
}
if (line != null) {
line.y2 = rect.y2;
line.x2 = rect.x2;
rects.add(line);
}
return rects;
}
public abstract static class Logger {
public abstract void log(String str);
}
public static class PrintLnLogger extends Logger {
@Override
public void log(String str) {
System.out.println(str);
}
}
}
@@ -0,0 +1,37 @@
package mobi.librera.smartreflow;
public interface PlatformImage<T> {
int[] RED = new int[]{255, 0, 0};
int[] GREEN = new int[]{0, 255, 0};
int[] BLUE = new int[]{0, 0, 255};
int[] WHITE = new int[]{255, 255, 255};
int[] BLACK = new int[]{0, 0, 0};
int[] YELLOW = new int[]{128, 255, 0};
int[] MAROON = new int[]{128, 0, 0};
void create(int width, int height);
void load(T img);
int getWidth();
int getHeight();
void setPixel(int x, int y, int color[]);
void load(String path) throws Exception;
T getImage();
int[] getPixel(int x, int y);
int getRedPixel(int x, int y);
boolean isBlackPixel(int x, int y);
}
@@ -0,0 +1,211 @@
package mobi.librera.smartreflow;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileFilter;
public class SmartImageRunUI {
static GraphicsConfiguration gc;
//static String img = "/home/ivan-dev/Downloads/testSM/2.jpg";
static String img = "/home/ivan-dev/IdeaProjects/SmartReflow/images/sample1.png";
public static void main(String[] args) throws Exception {
runIU(img);
}
private static JFrame runIU(String path) throws Exception {
AwtPlatformImage image = new AwtPlatformImage(path);
SmartReflow1 reflow = new SmartReflow1();
reflow.process(image);
// reflow.isDrawResult = true;
// reflow.isDrawResultUsingWords = true;
//
// reflow.isDrawColums = true;
// reflow.isDrawLines = false;
// reflow.isDrawWords = true;
// reflow.isDrawChars = false;
// reflow.isDrawWordsOffsetLeft = true;
AwtPlatformImage out = new AwtPlatformImage();
reflow.drawObjects(out);
final JFrame frame = new JFrame(gc);
JPanel horizontal = new JPanel();
horizontal.setLayout(new BoxLayout(horizontal, BoxLayout.LINE_AXIS));
JPanel vertical = new JPanel();
vertical.setBorder(new EmptyBorder(10, 10, 10, 10));
vertical.setLayout(new BoxLayout(vertical, BoxLayout.PAGE_AXIS));
vertical.setAlignmentY(Component.TOP_ALIGNMENT);
//vertical.add(makeLabel("Original Image"));
//Image originalImage = image.getImage();
//if (originalImage != null) {
//vertical.add(new JLabel(new ImageIcon(originalImage)));
//}
Image objects = out.getImage();
vertical.add(makeLabel("Objects " + out.getWidth() + "x" + out.getHeight()));
if (objects != null) {
try {
vertical.add(new JLabel(new ImageIcon(objects)));
} catch (Exception e) {
e.printStackTrace();
}
}
vertical.add(makeLabel("Statistics"));
for (String line : reflow.getStatistics()) {
vertical.add(makeText(line));
}
JPanel vertical2 = new JPanel();
vertical2.setAlignmentY(Component.TOP_ALIGNMENT);
vertical2.setBorder(new EmptyBorder(10, 10, 10, 10));
vertical2.setLayout(new BoxLayout(vertical2, BoxLayout.PAGE_AXIS));
try {
final int w = 407;
AwtPlatformImage out2 = new AwtPlatformImage(w, w * 2);
reflow.reflow(out2);
vertical2.add(makeLabel("Reflow " + out2.getWidth() + "x" + out2.getHeight()));
Image reflowed = out2.getImage();
if (reflowed != null) {
vertical2.add(new JLabel(new ImageIcon(reflowed)));
}
} catch (Exception e) {
vertical2.add(makeLabel("Error : " + e.getMessage()));
e.printStackTrace();
}
horizontal.add(vertical);
horizontal.add(vertical2);
JPanel main = new JPanel();
main.setAlignmentY(Component.TOP_ALIGNMENT);
main.setLayout(new BoxLayout(main, BoxLayout.PAGE_AXIS));
final JButton open1 = new JButton("Open");
open1.setAlignmentX(Component.LEFT_ALIGNMENT);
open1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
File my = new File("/home/ivan-dev/IdeaProjects/SmartReflow/src1");
if (!my.exists()) {
my = new File(System.getProperty("user.dir"));
}
JFileChooser fileChooser = null;
if (my.exists()) {
fileChooser = new JFileChooser(my);
} else {
fileChooser = new JFileChooser();
}
fileChooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || f.getPath().endsWith(".png") || f.getPath().endsWith(".jpg");
}
@Override
public String getDescription() {
return null;
}
});
int res = fileChooser.showOpenDialog(open1);
if (res == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
//open.setText(file.getPath());
//frame.dispose();
//frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
frame.setVisible(false);
try {
runIU(file.getPath());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
});
main.add(open1);
main.add(new JScrollPane(horizontal));
frame.add(main);
frame.setTitle("Librera Native Reflow");
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setSize((int) screenSize.getWidth(), (int) screenSize.getHeight());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent ke) { // handler
if (ke.getKeyCode() == ke.VK_ESCAPE) {
frame.dispose();
} else {
}
}
});
return frame;
}
private static JLabel makeLabel(String txt) {
JLabel originalImage = new JLabel(txt);
originalImage.setFont(new Font("Serif", Font.BOLD, 28));
return originalImage;
}
private static JLabel makeText(String txt) {
JLabel originalImage = new JLabel(txt);
originalImage.setFont(new Font("Monospaced", Font.PLAIN, 28));
return originalImage;
}
}
@@ -0,0 +1,426 @@
package mobi.librera.smartreflow;
import java.util.ArrayList;
import java.util.List;
import mobi.librera.smartreflow.model.Column;
import mobi.librera.smartreflow.model.Line;
import mobi.librera.smartreflow.model.Word;
public class SmartReflow implements SmartReflowInterface {
public static final int MIN_WORD_SIZE = 5;
public static final int MIN_LINE_HEIGHT = 10;
public static int PADDING = 15;
public boolean isDrawColums;
public boolean isDrawLines;
public boolean isDrawWords;
public boolean isDrawWordsOffsetLeft;
public boolean isDrawChars;
public boolean isDrawResultUsingWords = true;
float averageTop = 0;
int averageTopCount = 0;
List<Column> columns = new ArrayList<>();
List<Line> lines = new ArrayList<>();
List<Word> words = new ArrayList<Word>();
List<Word> wordsLong = new ArrayList<Word>();
boolean isDrawResult = true;
PlatformImage image;
public SmartReflow(PlatformImage image) throws Exception {
this.image = image;
process(image);
}
public boolean isMultyColumn() {
return columns.size() >= 2;
}
public List<String> getStatistics() {
List<String> list = new ArrayList<>();
list.add("Columns :" + columns.size());
list.add("Lines :" + lines.size());
list.add("Words :" + words.size());
list.add("Padding :" + PADDING);
int sum = 0;
for (Word w : wordsLong) {
sum += w.offsetLeft;
}
list.add("Mid offset :" + sum / wordsLong.size());
return list;
}
@Override
public void process(PlatformImage img) throws Exception {
ImageUtils.log("img", "w", img.getWidth(), "h", img.getHeight());
PADDING = img.getWidth() / 200;
System.out.println("PADDING " + PADDING + " k= " + (int) img.getWidth() / 15);
columns.clear();
lines.clear();
words.clear();
//detect colums
boolean isColumn = true;
int center = img.getWidth() / 2;
for (int dx = -PADDING / 2; dx < PADDING / 2; dx++) {
for (int y = 0; y < img.getHeight(); y++) {
if (img.isBlackPixel(center + dx, y)) {
isColumn = false;
}
}
if (isColumn) {
break;
}
}
Column column = null;
int number = 0;
for (int x = 0; x < img.getWidth(); x++) {
isColumn = false;
for (int y = 0; y < img.getHeight(); y++) {
if (img.isBlackPixel(x, y)) {
isColumn = true;
}
if (isColumn && column == null) {
column = new Column();
column.number = number++;
column.x1 = x;
column.y1 = 0;
break;
}
}
if (column != null && !isColumn) {
column.x2 = x;
column.y2 = img.getHeight() - 1;
columns.add(column);
column = null;
}
}
System.out.println("Columns size" + columns.size());
for (Column col : columns) {
// while (isLineWhite(col.y1, col.x1, col.x2)) {
// col.y1++;
// }
//
// while (isLineWhite(col.y2, col.x1, col.x2)) {
// col.y2--;
// }
}
for (Column col : columns) {
proccessColumn(img, col);
}
Word word = null;
Word prevCh = new Word();
for (Word ch : words) {
if (ch.isFirstWord || ch.offsetLeft > PADDING) {
if (word == null) {
word = new Word();
word.isFirstWord = ch.isFirstWord;
word.offsetLeft = ch.offsetLeft;
word.offsetTop = ch.offsetTop;
word.x1 = ch.x1;
word.y1 = ch.y1;
} else if (word != null) {
word.y2 = prevCh.y2;
word.x2 = prevCh.x2;
wordsLong.add(word);
word = new Word();
word.x1 = ch.x1;
word.y1 = ch.y1;
word.isFirstWord = ch.isFirstWord;
word.offsetLeft = ch.offsetLeft;
word.offsetTop = ch.offsetTop;
}
} else if (ch.isLastWord) {
word.y2 = ch.y2;
word.x2 = ch.x2;
wordsLong.add(word);
word = null;
}
prevCh = ch;
}
if (word != null) {
wordsLong.add(prevCh);
}
}
public void proccessColumn(PlatformImage img, Column col) {
averageTopCount = 0;
averageTop = 0;
//detect lines
Line findLine = null;
for (int y = col.y1; y < col.y2; y++) {
boolean isBank = isBlackHorizontal(img, y, col.x1, col.x2);
if (!isBank && findLine == null) {
findLine = new Line();
findLine.columnNumber = col.number;
findLine.y1 = y;
findLine.x1 = col.x1;
} else if (isBank && findLine != null) {
findLine.y2 = y;
findLine.x2 = col.x2;
if (findLine.height() > MIN_LINE_HEIGHT) {
lines.add(findLine);
findLine = null;
}
}
}
if (findLine != null) {
findLine.y2 = col.y2;
findLine.x2 = col.x2;
lines.add(findLine);
}
//dect words
Line prevLine = new Line();
prevLine.y2 = col.y1;
for (Line line : lines) {
if (line.columnNumber != col.number) {
continue;
}
Word word = null;
Word prevWord = new Word();
prevWord.x2 = col.x1;
boolean isFirstWord = true;
for (int x = col.x1; x < col.x2; x++) {
boolean isBlank = isBlankVertical(img, x, line.y1, line.y2);
if (!isBlank && word == null) {
word = new Word();
word.x1 = x;
word.y1 = line.y1;
word.isFirstWord = isFirstWord;
word.offsetLeft = word.x1 - prevWord.x2;
word.offsetTop = line.y1 - prevLine.y2;
if (isFirstWord) {
averageTop += word.offsetTop;
averageTopCount++;
}
isFirstWord = false;
} else if (isBlank && word != null) {
word.x2 = x;
word.y2 = line.y2;
if (word.width() > MIN_WORD_SIZE || word.height() > MIN_WORD_SIZE) {
words.add(word);
prevWord = word;
}
word = null;
}
}
if (word != null) {
word.x2 = col.x2;
word.y2 = line.y2;
words.add(word);
}
words.get(words.size() - 1).isLastWord = true;
prevLine = line;
}
averageTop = averageTop / averageTopCount;
}
@Override
public void drawObjects(PlatformImage output) {
output.create(image.getWidth(), image.getHeight());
ImageUtils.log("Columns:", columns.size());
ImageUtils.log("Lines:", lines.size());
ImageUtils.log("Words:", words.size());
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
output.setPixel(x, y, image.getPixel(x, y));
}
}
if (isDrawColums) {
for (Column col : columns) {
ImageUtils.drawRect(output, col.x1, col.y1, col.x2, col.y2, PlatformImage.GREEN);
}
}
if (isDrawLines) {
for (Line l : lines) {
ImageUtils.drawRect(output, l.x1, l.y1, l.x2, l.y2, PlatformImage.RED);
}
}
for (Word w : words) {
if (isDrawChars) {
ImageUtils.drawRect(output, w.x1, w.y1, w.x2, w.y2, PlatformImage.BLUE);
}
int dy = w.y1;
ImageUtils.drawRect(output, w.x1 - w.offsetLeft, dy, w.x1, dy + 2, PlatformImage.YELLOW);
}
for (Word w : wordsLong) {
ImageUtils.drawRect(output, w.x1, w.y1, w.x2, w.y2, PlatformImage.MAROON);
}
}
@Override
public void reflow(PlatformImage des) {
if (columns.isEmpty()) {
des.create(image.getWidth(), image.getHeight());
for (int x = 0; x < des.getWidth(); x++) {
for (int y = 0; y < des.getHeight(); y++) {
des.setPixel(x, y, image.getPixel(x, y));
}
}
return;
}
ImageUtils.setBackgroundColor(des, PlatformImage.WHITE);
int dx = PADDING;
int dy = 0;
Word prev = new Word();
List<Word> input;
boolean isDrawResult = true;
//if (isDrawResultUsingWords) {
if (isDrawResultUsingWords) {
input = wordsLong;
} else {
input = words;
}
for (Word w : input) {
if (w.height() > des.getHeight()) {
//w.y2 = w.y2 / 2;
}
if (w.width() > des.getWidth()) {
//w.x2 = w.x2
}
dx = dx + w.offsetLeft;
if (w.height() != prev.height() && prev.height() > 0) {
dy -= w.height() - prev.height();
}
//chapter
if (w.isFirstWord) {
//w.offsetTop = PADDING;
//ImageUtils.log("w.offsetLeft", w.offsetLeft);
}
if (w.isFirstWord && (w.offsetLeft > PADDING * 2 || w.offsetTop > PADDING + 5)) {
dx = w.offsetLeft + PADDING;
dy += w.height() + w.offsetTop;
} else if (w.isFirstWord) {
dx += Math.max(w.offsetLeft, PADDING);
}
//new line
if (w.width() + dx > des.getWidth() - PADDING) {
dx = PADDING;
dy += w.height() + averageTop;
}
try {
drawWordAt(w, dx, dy, image, des);
dx += w.width();
prev = w;
} catch (Exception e) {
//e.printStackTrace();
//continue;
break;
}
}
}
public void drawWordAt(Word w, int dx, int dy, PlatformImage input, PlatformImage output) {
for (int i = w.x1; i < w.x2; i++) {
for (int j = w.y1; j < w.y2; j++) {
int pixel[] = input.getPixel(i, j);
final int x = dx + (i - w.x1);
final int y = dy + (j - w.y1);
output.setPixel(x, y, pixel);
}
}
}
public boolean isBlackHorizontal(PlatformImage img, int y, int x1, int x2) {
boolean isBlank = true;
for (int x = x1; x < x2; x++) {
if (img.isBlackPixel(x, y)) {
isBlank = false;
break;
}
}
return isBlank;
}
public boolean isBlankVertical(PlatformImage img, int x, int y1, int y2) {
boolean isBlank = true;
for (int y = y1; y < y2; y++) {
if (img.isBlackPixel(x, y)) {
isBlank = false;
break;
}
}
return isBlank;
}
}
@@ -0,0 +1,144 @@
package mobi.librera.smartreflow;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import mobi.librera.smartreflow.model.Rect;
import mobi.librera.smartreflow.model.Word;
public class SmartReflow1 implements SmartReflowInterface {
PlatformImage img;
List<Rect> lines = new ArrayList<Rect>();
List<Word> words = new ArrayList<Word>();
int minLineHeight = 5;
int minSpaceSize = 5;
boolean isTwoColumns = true;
@Override
public void process(PlatformImage img) throws Exception {
this.img = img;
lines.clear();
words.clear();
int center = img.getWidth() / 2;
for (int dx = -minSpaceSize; dx < minSpaceSize; dx++) {
for (int y = 0; y < img.getHeight(); y++) {
if (img.isBlackPixel(center + dx, y)) {
isTwoColumns = false;
break;
}
}
if (isTwoColumns) {
break;
}
}
if (isTwoColumns) {
lines.addAll(ImageUtils.splitHorizontal(img, new Rect(0, 0, img.getWidth() / 2, img.getHeight() - 1), minLineHeight));
lines.addAll(ImageUtils.splitHorizontal(img, new Rect(img.getWidth() / 2, 0, img.getWidth() - 1, img.getHeight() - 1), minLineHeight));
} else {
lines.addAll(ImageUtils.splitHorizontal(img, new Rect(0, 0, img.getWidth() - 1, img.getHeight() - 1), minLineHeight));
}
//accurate lines
for (Rect l : lines) {
ImageUtils.removeWhiteBegin(img, l);
ImageUtils.removeWhiteEnd(img, l);
}
for (Rect l : lines) {
final List<Word> all = ImageUtils.splitVertical(img, l, minSpaceSize);
if (!all.isEmpty()) {
final Word first = all.get(0);
first.isFirstWord = true;
}
words.addAll(all);
}
for (Rect l : words) {
ImageUtils.removeWhiteBegin(img, l);
ImageUtils.removeWhiteEnd(img, l);
}
}
@Override
public void drawObjects(PlatformImage out) {
out.create(img.getWidth(), img.getHeight());
ImageUtils.copyRect(img, out, 0, 0, img.getWidth(), img.getHeight());
for (Rect r : lines) {
ImageUtils.drawRect(out, r.x1, r.y1, r.x2, r.y2, PlatformImage.YELLOW);
}
for (Rect r : words) {
ImageUtils.drawRect(out, r.x1, r.y1, r.x2, r.y2, PlatformImage.BLUE);
}
}
@Override
public void reflow(PlatformImage out) {
if (isTwoColumns) {
//out.create(img.getWidth() / 2, img.getHeight() * 2);
} else {
//out.create(img.getWidth() / 2, img.getHeight());
}
ImageUtils.setBackgroundColor(out, PlatformImage.WHITE);
Word prev = words.get(0);
int padding = 10;
int indent = 5;
int space = 10;
int dx = padding;
int dy = padding;
for (Word w : words) {
if (w.isFirstWord) {
int dh = w.y1 - prev.y2;
if (dh > prev.height() ) {
dy += dh + prev.height();
dx = padding;
}
}
if (Math.abs(w.height() - prev.height()) > prev.height() / 2) {
dx = padding;
dy += prev.height();
}
if (dx + w.width() + padding > out.getWidth()) {
dx = padding;
dy += prev.height() + indent;
}
if (dy + w.height() + padding > out.getHeight()) {
break;
}
ImageUtils.drawWord(w, dx, dy, img, out);
dx = dx + w.width() + space;
prev = w;
}
}
@Override
public List<String> getStatistics() {
return Arrays.asList("Lines: " + lines.size());
}
}
@@ -0,0 +1,17 @@
package mobi.librera.smartreflow;
import java.util.List;
public interface SmartReflowInterface {
void process(PlatformImage img) throws Exception;
void drawObjects(PlatformImage output);
void reflow(PlatformImage des);
List<String> getStatistics();
}
@@ -0,0 +1,76 @@
package mobi.librera.smartreflow;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TestImage {
static String img = "/home/ivan-dev/IdeaProjects/SmartReflow/images/sample6.png";
static GraphicsConfiguration gc;
public static void main(String[] args) throws Exception {
final JFrame frame = new JFrame(gc);
final AwtPlatformImage input = new AwtPlatformImage(img);
final AwtPlatformImage output = new AwtPlatformImage(600,1000);
SmartReflow sm = new SmartReflow(input);
sm.reflow(output);
frame.add(new JLabel(new ImageIcon(output.getImage())));
frame.setTitle("Librera Native Reflow");
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setSize((int) screenSize.getWidth(), (int) screenSize.getHeight());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent ke) { // handler
if (ke.getKeyCode() == ke.VK_ESCAPE) {
frame.dispose();
} else {
}
}
});
}
public static void test1(PlatformImage image, PlatformImage output) throws Exception {
image.create(200, 200);
//image.load(img);
ImageUtils.setBackgroundColor(image, PlatformImage.WHITE);
for (int x = 0; x < 200; x++) {
image.setPixel(x, x, PlatformImage.RED);
}
ImageUtils.drawRect(image, 10, 10, 100, 100, PlatformImage.BLUE);
output.create(400, 400);
ImageUtils.copyRect(image, output, 0, 0, 150, 150);
}
}
@@ -0,0 +1,5 @@
package mobi.librera.smartreflow.model;
public class Column extends Rect {
public int number;
}
@@ -0,0 +1,18 @@
package mobi.librera.smartreflow.model;
public class Line extends Rect {
public int columnNumber;
public Line() {
}
public Line(int x, int y) {
super(x, y);
}
public Line(int x, int y, int x1, int y1) {
super(x, y, x1, y1);
}
}
@@ -0,0 +1,32 @@
package mobi.librera.smartreflow.model;
public class Rect {
public int x1;
public int y1;
public int x2;
public int y2;
public Rect() {
}
public Rect(int x, int y) {
this.x1 = x;
this.y1 = y;
}
public Rect(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public int width() {
return x2 - x1;
}
public int height() {
return y2 - y1;
}
}
@@ -0,0 +1,17 @@
package mobi.librera.smartreflow.model;
public class Word extends Rect {
public int offsetLeft;
public int offsetTop;
public boolean isFirstWord;
public boolean isLastWord;
public Word() {
}
public Word(int x, int y) {
super(x, y);
}
}