Related
I have this .java datafile. The data file is a part of an imagej plugin.
The whole data structure is here:
enter link description here
package mosaic.plugins;
import ij.IJ;
import ij.ImagePlus;
import ij.macro.Interpreter;
import ij.measure.ResultsTable;
import ij.process.ByteProcessor;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import mosaic.plugins.utils.PlugIn8bitBase;
import net.imglib2.Cursor;
import net.imglib2.IterableInterval;
import net.imglib2.RandomAccess;
import net.imglib2.img.ImagePlusAdapter;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
import net.imglib2.img.array.ArrayImgFactory;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.NumericType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.integer.UnsignedByteType;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.view.IntervalView;
import net.imglib2.view.Views;
public class Naturalization extends PlugIn8bitBase
{
// Precision in finding your best T
private static final float EPS = 0.0001f;
// Prior parameter for first oder
// In this case is for all channels
// Fixed parameter
private static final float T1_pr = 0.3754f;
// Number of bins for the Laplacian Histogram
// In general is 4 * N_Grad
// max of laplacian value is 4 * 255
private static final int N_Lap = 2041;
// Offset shift in the histogram bins
// Has to be N_Lap / 2;
private static final int Lap_Offset = 1020;
// Number of bins for the Gradient
private static final int N_Grad = 512;
// Offset for the gradient histogram shift
private static final int Grad_Offset = 256;
// Prior parameter for second order (Parameters learned from trained data set)
// For different color R G B
// For one channel image use an average of them
private final float T2_pr[] = {0.2421f ,0.2550f, 0.2474f, 0.24816666f};
// Keeps values of PSNR for all images and channels in case of RGB. Maps: imageNumber -> map (channel, PSNR value)
private final Map<Integer, Map<Integer, Float>> iPsnrOutput = new TreeMap<Integer, Map<Integer, Float>>();
private synchronized void addPsnr(int aSlice, int aChannel, float aValue) {
Map<Integer, Float> map = iPsnrOutput.get(aSlice);
boolean isNewMap = false;
if (map == null) {
map = new TreeMap<Integer, Float>();
isNewMap = true;
}
map.put(aChannel, aValue);
if (isNewMap) {
iPsnrOutput.put(aSlice, map);
}
}
#Override
protected void processImg(ByteProcessor aOutputImg, ByteProcessor aOrigImg, int aChannelNumber) {
// perform naturalization
final ImagePlus naturalizedImg = naturalize8bitImage(aOrigImg, aChannelNumber);
// set processed pixels to output image
aOutputImg.setPixels(naturalizedImg.getProcessor().getPixels());
}
#Override
protected void postprocessBeforeShow() {
// Create result table with all stored PSNRs.
final ResultsTable rs = new ResultsTable();
for (final Entry<Integer, Map<Integer, Float>> e : iPsnrOutput.entrySet()) {
rs.incrementCounter();
for (final Entry<Integer, Float> m : e.getValue().entrySet()) {
switch(m.getKey()) {
case CHANNEL_R: rs.addValue("Naturalization R", m.getValue()); rs.addValue("Estimated R PSNR", calculate_PSNR(m.getValue())); break;
case CHANNEL_G: rs.addValue("Naturalization G", m.getValue()); rs.addValue("Estimated G PSNR", calculate_PSNR(m.getValue())); break;
case CHANNEL_B: rs.addValue("Naturalization B", m.getValue()); rs.addValue("Estimated B PSNR", calculate_PSNR(m.getValue())); break;
case CHANNEL_8G: rs.addValue("Naturalization", m.getValue()); rs.addValue("Estimated PSNR", calculate_PSNR(m.getValue())); break;
default: break;
}
}
}
if (!Interpreter.isBatchMode()) {
rs.show("Naturalization and PSNR");
showMessage();
}
}
private ImagePlus naturalize8bitImage(ByteProcessor imp, int aChannelNumber) {
Img<UnsignedByteType> TChannel = ImagePlusAdapter.wrap(new ImagePlus("", imp));
final float T2_prior = T2_pr[(aChannelNumber <= CHANNEL_B) ? 2-aChannelNumber : CHANNEL_8G];
final float[] result = {0.0f}; // ugly but one of ways to get result back via parameters;
// Perform naturalization and store PSNR result. Finally return image in ImageJ format.
TChannel = performNaturalization(TChannel, T2_prior, result);
addPsnr(imp.getSliceNumber(), aChannelNumber, result[0]);
return ImageJFunctions.wrap(TChannel,"temporaryName");
}
/**
* Naturalize the image
* #param Img original image
* #param Theta parameter
* #param Class<T> Original image
* #param Class<S> Calculation Type
* #param T2_prior Prior to use
* #param result One element array to store nautralization factor
*/
private <T extends NumericType<T> & NativeType<T> & RealType<T>, S extends RealType<S>> Img<T> doNaturalization(Img<T> image_orig, S Theta,Class<T> cls_t, float T2_prior, float[] result) throws InstantiationException, IllegalAccessException
{
if (image_orig == null) {return null;}
// Check that the image data set is 8 bit
// Otherwise return an error or hint to scale down
final T image_check = cls_t.newInstance();
final Object obj = image_check;
if (!(obj instanceof UnsignedByteType)) {
IJ.error("Error it work only with 8-bit type");
return null;
}
final float Nf = findNaturalizationFactor(image_orig, Theta, T2_prior);
result[0] = Nf;
final Img<T> image_result = naturalizeImage(image_orig, Nf, cls_t);
return image_result;
}
private <S extends RealType<S>, T extends NumericType<T> & NativeType<T> & RealType<T>>
Img<T> naturalizeImage(Img<T> image_orig, float Nf, Class<T> cls_t)
throws InstantiationException, IllegalAccessException
{
// Mean of the original image
// S mean_original = cls_s.newInstance();
// Mean<T,S> m = new Mean<T,S>();
// m.compute(image_orig.cursor(), mean_original);
// TODO: quick fix for deprecated code above. Is new 'mean' utility introduced in imglib2?
float mean_original = 0.0f;
final Cursor<T> c2 = image_orig.cursor();
float count = 0.0f;
while (c2.hasNext()) {
c2.next();
mean_original += c2.get().getRealFloat();
count += 1.0f;
}
mean_original /= count;
// Create result image
final long[] origImgDimensions = new long[2];
image_orig.dimensions(origImgDimensions);
final Img<T> image_result = image_orig.factory().create(origImgDimensions, cls_t.newInstance());
// for each pixel naturalize
final Cursor<T> cur_orig = image_orig.cursor();
final Cursor<T> cur_ir = image_result.cursor();
while (cur_orig.hasNext()) {
cur_orig.next();
cur_ir.next();
final float tmp = cur_orig.get().getRealFloat();
// Naturalize
float Nat = (int) ((tmp - mean_original)*Nf + mean_original + 0.5);
if (Nat < 0)
{Nat = 0;}
else if (Nat > 255)
{Nat = 255;}
cur_ir.get().setReal(Nat);
}
return image_result;
}
private <S extends RealType<S>, T extends NumericType<T> & NativeType<T> & RealType<T>> float findNaturalizationFactor(Img<T> image_orig, S Theta, float T2prior) {
final ImgFactory<FloatType> imgFactoryF = new ArrayImgFactory<FloatType>();
// Create one dimensional image (Histogram)
final Img<FloatType> LapCDF = imgFactoryF.create(new long[] {N_Lap}, new FloatType());
// Two dimensional image for Gradient
final Img<FloatType> GradCDF = imgFactoryF.create(new long[] {N_Grad, 2}, new FloatType());
// GradientCDF = Integral of the histogram of the of the Gradient field
// LaplacianCDF = Integral of the Histogram of the Laplacian field
final Img<FloatType> GradD = create2DGradientField();
calculateLaplaceFieldAndGradient(image_orig, LapCDF, GradD);
convertGrad2dToCDF(GradD);
calculateGradCDF(GradCDF, GradD);
calculateLapCDF(LapCDF);
// For each channel find the best T1
// EPS=precision
// for X component
float T_tmp = (float)FindT(Views.iterable(Views.hyperSlice(GradCDF, GradCDF.numDimensions()-1 , 0)), N_Grad, Grad_Offset, EPS);
// for Y component
T_tmp += FindT(Views.iterable(Views.hyperSlice(GradCDF, GradCDF.numDimensions()-1 , 1)), N_Grad, Grad_Offset, EPS);
// Average them and divide by the prior parameter
final float T1 = T_tmp/(2*T1_pr);
// Find the best parameter and divide by the T2 prior
final float T2 = (float)FindT(LapCDF, N_Lap, Lap_Offset, EPS)/T2prior;
// Calculate naturalization factor!
final float Nf = (float) ((1.0-Theta.getRealDouble())*T1 + Theta.getRealDouble()*T2);
return Nf;
}
/**
* Calculate the peak SNR from the Naturalization factor
*
* #param Nf naturalization factor
* #return the PSNR
*/
String calculate_PSNR(double x)
{
if (x >= 0 && x <= 0.934)
{
return String.format("%.2f", new Float(23.65 * Math.exp(0.6 * x) - 20.0 * Math.exp(-7.508 * x)));
}
else if (x > 0.934 && x < 1.07)
{
return new String("> 40");
}
else if (x >= 1.07 && x < 1.9)
{
return String.format("%.2f", new Float(-11.566 * x + 52.776));
}
else
{
return String.format("%.2f",new Float(13.06*x*x*x*x - 121.4 * x*x*x + 408.5 * x*x -595.5*x + 349));
}
}
private Img<UnsignedByteType> performNaturalization(Img<UnsignedByteType> channel, float T2_prior, float[] result) {
// Parameters balance between first order and second order
final FloatType Theta = new FloatType(0.5f);
try {
channel = doNaturalization(channel, Theta, UnsignedByteType.class, T2_prior, result);
} catch (final InstantiationException e) {
e.printStackTrace();
} catch (final IllegalAccessException e) {
e.printStackTrace();
}
return channel;
}
// Original data
// N = nuber of bins
// offset of the histogram
// T current
private double FindT_Evalue(float[] p_d, int N, int offset, float T)
{
double error = 0;
for (int i=-offset; i<N-offset; ++i) {
final double tmp = Math.atan(T*(i)) - p_d[i+offset];
error += (tmp*tmp);
}
return error;
}
// Find the T
// data CDF Histogram
// N number of bins
// Offset of the histogram
// eps precision
private double FindT(IterableInterval<FloatType> data, int N, int OffSet, float eps)
{
//find the best parameter between data and model atan(Tx)/pi+0.5
// Search between 0 and 1.0
float left = 0;
float right = 1.0f;
float m1 = 0.0f;
float m2 = 0.0f;
// Crate p_t to save computation (shift and rescale the original CDF)
final float p_t[] = new float[N];
// Copy the data
final Cursor<FloatType> cur_data = data.cursor();
for (int i = 0; i < N; ++i)
{
cur_data.next();
p_t[i] = (float) ((cur_data.get().getRealFloat() - 0.5)*Math.PI);
}
// While the precision is bigger than eps
while (right-left>=eps)
{
// move left and right of 1/3 (m1 and m2)
m1=left+(right-left)/3;
m2=right-(right-left)/3;
// Evaluate on m1 and m2, ane move the extreme point
if (FindT_Evalue(p_t, N, OffSet, m1) <=FindT_Evalue(p_t, N, OffSet, m2)) {
right=m2;
}
else {
left=m1;
}
}
// return the average
return (m1+m2)/2;
}
private Img<FloatType> create2DGradientField() {
final long dims[] = new long[2];
dims[0] = N_Grad;
dims[1] = N_Grad;
final Img<FloatType> GradD = new ArrayImgFactory<FloatType>().create(dims, new FloatType());
return GradD;
}
private void calculateLapCDF(Img<FloatType> LapCDF) {
final RandomAccess<FloatType> Lap_hist2 = LapCDF.randomAccess();
//convert Lap to CDF
for (int i = 1; i < N_Lap; ++i)
{
Lap_hist2.setPosition(i-1,0);
final float prec = Lap_hist2.get().getRealFloat();
Lap_hist2.move(1,0);
Lap_hist2.get().set(Lap_hist2.get().getRealFloat() + prec);
}
}
private void calculateGradCDF(Img<FloatType> GradCDF, Img<FloatType> GradD) {
final RandomAccess<FloatType> Grad_dist = GradD.randomAccess();
// Gradient on x pointer
final IntervalView<FloatType> Gradx = Views.hyperSlice(GradCDF, GradCDF.numDimensions()-1 , 0);
// Gradient on y pointer
final IntervalView<FloatType> Grady = Views.hyperSlice(GradCDF, GradCDF.numDimensions()-1 , 1);
integrateOverRowAndCol(Grad_dist, Gradx, Grady);
scaleGradiens(Gradx, Grady);
}
private void scaleGradiens(IntervalView<FloatType> Gradx, IntervalView<FloatType> Grady) {
final RandomAccess<FloatType> Gradx_r2 = Gradx.randomAccess();
final RandomAccess<FloatType> Grady_r2 = Grady.randomAccess();
//scale, divide the number of integrated bins
for (int i = 0; i < N_Grad; ++i)
{
Gradx_r2.setPosition(i,0);
Grady_r2.setPosition(i,0);
Gradx_r2.get().set((float) (Gradx_r2.get().getRealFloat() / 255.0));
Grady_r2.get().set((float) (Grady_r2.get().getRealFloat() / 255.0));
}
}
private void integrateOverRowAndCol(RandomAccess<FloatType> Grad_dist, IntervalView<FloatType> Gradx, IntervalView<FloatType> Grady) {
final int[] loc = new int[2];
// pGrad2D has 2D CDF
final RandomAccess<FloatType> Gradx_r = Gradx.randomAccess();
// Integrate over the row
for (int i = 0; i < N_Grad; ++i)
{
loc[1] = i;
Gradx_r.setPosition(i,0);
// get the row
for (int j = 0; j < N_Grad; ++j)
{
loc[0] = j;
// Set the position
Grad_dist.setPosition(loc);
// integrate over the row to get 1D vector
Gradx_r.get().set(Gradx_r.get().getRealFloat() + Grad_dist.get().getRealFloat());
}
}
final RandomAccess<FloatType> Grady_r = Grady.randomAccess();
// Integrate over the column
for (int i = 0; i < N_Grad; ++i)
{
loc[1] = i;
Grady_r.setPosition(0,0);
for (int j = 0; j < N_Grad; ++j)
{
loc[0] = j;
Grad_dist.setPosition(loc);
Grady_r.get().set(Grady_r.get().getRealFloat() + Grad_dist.get().getRealFloat());
Grady_r.move(1,0);
}
}
}
private <T extends RealType<T>> void calculateLaplaceFieldAndGradient(Img<T> image, Img<FloatType> LapCDF, Img<FloatType> GradD) {
final RandomAccess<FloatType> Grad_dist = GradD.randomAccess();
final long[] origImgDimensions = new long[2];
image.dimensions(origImgDimensions);
final Img<FloatType> laplaceField = new ArrayImgFactory<FloatType>().create(origImgDimensions, new FloatType());
// Cursor localization
final int[] indexD = new int[2];
final int[] loc_p = new int[2];
final RandomAccess<T> img_cur = image.randomAccess();
final RandomAccess<FloatType> Lap_f = laplaceField.randomAccess();
final RandomAccess<FloatType> Lap_hist = LapCDF.randomAccess();
// Normalization 1/(Number of pixel of the original image)
long n_pixel = 1;
for (int i = 0 ; i < laplaceField.numDimensions() ; i++)
{n_pixel *= laplaceField.dimension(i)-2;}
// unit to sum
final double f = 1.0/(n_pixel);
// Inside the image for Y
final Cursor<FloatType> cur = laplaceField.cursor();
// For each point of the Laplacian field
while (cur.hasNext())
{
cur.next();
// Localize cursors
cur.localize(loc_p);
// Exclude the border
boolean border = false;
for (int i = 0 ; i < image.numDimensions() ; i++)
{
if (loc_p[i] == 0)
{border = true;}
else if (loc_p[i] == image.dimension(i)-1)
{border = true;}
}
if (border == true) {
continue;
}
// get the stencil value;
img_cur.setPosition(loc_p);
float L = -4*img_cur.get().getRealFloat();
// Laplacian
for (int i = 0 ; i < 2 ; i++)
{
img_cur.move(1, i);
final float G_p = img_cur.get().getRealFloat();
img_cur.move(-1,i);
final float G_m = img_cur.get().getRealFloat();
img_cur.move(-1, i);
final float L_m = img_cur.get().getRealFloat();
img_cur.setPosition(loc_p);
L += G_p + L_m;
// Calculate the gradient + convert into bin
indexD[1-i] = (int) (Grad_Offset + G_p - G_m);
}
Lap_f.setPosition(loc_p);
// Set the Laplacian field
Lap_f.get().setReal(L);
// Histogram bin conversion
L += Lap_Offset;
Lap_hist.setPosition((int)(L),0);
Lap_hist.get().setReal(Lap_hist.get().getRealFloat() + f);
Grad_dist.setPosition(indexD);
Grad_dist.get().setReal(Grad_dist.get().getRealFloat() + f);
}
}
private void convertGrad2dToCDF(Img<FloatType> GradD) {
final RandomAccess<FloatType> Grad_dist = GradD.randomAccess();
final int[] loc = new int[GradD.numDimensions()];
// for each row
for (int j = 0; j < GradD.dimension(1); ++j)
{
loc[1] = j;
for (int i = 1; i < GradD.dimension(0) ; ++i)
{
loc[0] = i-1;
Grad_dist.setPosition(loc);
// Precedent float
final float prec = Grad_dist.get().getRealFloat();
// Move to the actual position
Grad_dist.move(1, 0);
// integration up to the current position
Grad_dist.get().set(Grad_dist.get().getRealFloat() + prec);
}
}
//col integration
for (int j = 1; j < GradD.dimension(1); ++j)
{
// Move to the actual position
loc[1] = j-1;
for (int i = 0; i < GradD.dimension(0); ++i)
{
loc[0] = i;
Grad_dist.setPosition(loc);
// Precedent float
final float prec = Grad_dist.get().getRealFloat();
// Move to the actual position
Grad_dist.move(1, 1);
Grad_dist.get().set(Grad_dist.get().getRealFloat() + prec);
}
}
}
/**
* Show information about authors and paper.
*/
private void showMessage()
{
// Create main window with panel to store gui components
final JDialog win = new JDialog((JDialog)null, "Naturalization", true);
final JPanel msg = new JPanel();
msg.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// Create message not editable but still focusable for copying
final JTextPane text = new JTextPane();
text.setContentType("text/html");
text.setText("<html>Y. Gong and I. F. Sbalzarini. Image enhancement by gradient distribution specification. In Proc. ACCV, <br>"
+ "12th Asian Conference on Computer Vision, Workshop on Emerging Topics in Image Enhancement and Restoration,<br>"
+ "pages w7–p3, Singapore, November 2014.<br><br>"
+ "Y. Gong and I. F. Sbalzarini, Gradient Distributions Priors for Biomedical Image Processing, 2014<br>http://arxiv.org/abs/1408.3300<br><br>"
+ "Y. Gong and I. F. Sbalzarini. A Natural-Scene Gradient Distribution Prior and its Application in Light-Microscopy Image Processing.<br>"
+ "IEEE Journal of Selected Topics in Signal Processing, Vol.10, No.1, February 2016, pages 99-114<br>"
+ "ISSN: 1932-4553, DOI: 10.1109/JSTSP.2015.2506122<br><br>"
+ "</html>");
text.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
text.setEditable(false);
msg.add(text);
// Add button "Close" for closing window easily
final JButton button = new JButton("Close");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
win.dispose();
}
});
msg.add(button);
// Finally show window with message
win.add(msg);
win.pack();
win.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
win.setVisible(true);
}
#Override
protected boolean showDialog() {
return true;
}
#Override
protected boolean setup(String aArgs) {
setFilePrefix("naturalized_");
return true;
}
}
I want it to compile it again and get a .class file or a whole .jar file of this plugin.
Which sturcuture and datas I need for get a .class data?
What are with the import files, where i can get the ij, java, javax and net files? In which structure must it be.
I am a novice in Java and only know, that the compiled command is javac.
on linux there is a command to do it which is javac
just : javac HelloWorld.java
it might be the same thing on windows but i am not sure (install a virtual linux box if there is no other way)
If something goes wrong google the error
If you want to compile a Java program from command line you should use the javac command and to invoke it just write java and then the name of your program.
Compiling a file you will have the .class file that you are looking for.
I have some code that takes 3 different PDF byte arrays and merges them. This code works great. The issue (some people) are having is that each PDF is considered to be a full page (if printed) even if there is only say 4 inches of content on it, thus leaving 7 inches of white space vertically. Then the middle document gets put in and may or may not have vertical white space at the end of it. Then the footer gets put on its own page as well.
Here is the code:
byte[] Bytes = rv.LocalReport.Render("PDF", null, out MimeType, out Encoding, out Extension, out StreamIDs, out Warnings);
List<byte[]> MergeSets = // This is filled prior to this code
// Append any other pages to this primary letter
if (MergeSets.Count > 0) {
MemoryStream ms = new MemoryStream();
Document document = new Document();
PdfCopy copy = new PdfCopy(document, ms);
document.Open();
PdfImportedPage page;
PdfReader reader = new PdfReader(Bytes); // read the generated primary Letter
int pages = reader.NumberOfPages;
for (int i = 0; i < pages; ) {
page = copy.GetImportedPage(reader, ++i);
copy.AddPage(page);
} // foreach of the pages in the Cover Letter
// Now append the merge sets
foreach (byte[] ba in MergeSets) {
reader = new PdfReader(ba);
pages = reader.NumberOfPages;
for (int i = 0; i < pages; ) {
page = copy.GetImportedPage(reader, ++i);
copy.AddPage(page);
} // foreach of the pages in the current merge set
} // foreach of the sets of data
document.Close();
ServerSaved = SaveGeneratedLetter(ms.GetBuffer(), DateTime.Now.Year, hl.LetterName, SaveName);
} // if there is anything to merge
Is there a way when I am merging each page to clip/remove/erase the vertical white space at the end of each pdf so it appears as one seamless document?
UPDATE:
Here are some sample .pdf files I am trying to merge.
header, body, footer
UPDATE 2: USING THE ANSWER:
I have converted #mkl's code to C# and here it is.
The tool class:
public class PdfVeryDenseMergeTool {
private Rectangle PageSize;
private float TopMargin;
private float BottomMargin;
private float Gap;
private Document Document = null;
private PdfWriter Writer = null;
private float YPosition = 0;
public PdfVeryDenseMergeTool(Rectangle size, float top, float bottom, float gap) {
this.PageSize = size;
this.TopMargin = top;
this.BottomMargin = bottom;
this.Gap = gap;
} // PdfVeryDenseMergeTool
public void Merge(MemoryStream outputStream, List<PdfReader> inputs) {
try {
this.OpenDocument(outputStream);
foreach (PdfReader reader in inputs) {
this.Merge(reader);
} // foreach of the PDF files to merge
} finally {
this.CloseDocument();
} // try-catch-finally
} // Merge
public void OpenDocument(MemoryStream outputStream) {
this.Document = new Document(PageSize, 36, 36, this.TopMargin, this.BottomMargin);
this.Writer = PdfWriter.GetInstance(Document, outputStream);
this.Document.Open();
this.NewPage();
} // OpenDocument
public void CloseDocument() {
try {
this.Document.Close();
} finally {
this.Document = null;
this.Writer = null;
this.YPosition = 0;
} // try-finally
} // CloseDocument
public void NewPage() {
this.Document.NewPage();
this.YPosition = PageSize.GetTop(this.TopMargin);
} // Merge
public void Merge(PdfReader reader) {
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
for (int pageIndex = 1; pageIndex <= reader.NumberOfPages; pageIndex++) {
this.Merge(reader, parser, pageIndex);
} // foreach of the pages of the current PDF
} // Merge
public void Merge(PdfReader reader, PdfReaderContentParser parser, int pageIndex) {
PdfImportedPage importedPage = Writer.GetImportedPage(reader, pageIndex);
PdfContentByte directContent = Writer.DirectContent;
PageVerticalAnalyzer finder = parser.ProcessContent(pageIndex, new PageVerticalAnalyzer());
if (finder.VerticalFlips.Count < 2)
return;
Rectangle pageSizeToImport = reader.GetPageSize(pageIndex);
int startFlip = finder.VerticalFlips.Count - 1;
bool first = true;
while (startFlip > 0) {
if (!first)
this.NewPage();
float freeSpace = this.YPosition - PageSize.GetBottom(BottomMargin);
int endFlip = startFlip + 1;
while ((endFlip > 1) && (finder.VerticalFlips[startFlip] - finder.VerticalFlips[endFlip - 2] < freeSpace))
endFlip -= 2;
if (endFlip < startFlip) {
float height = finder.VerticalFlips[startFlip] - finder.VerticalFlips[endFlip];
directContent.SaveState();
directContent.Rectangle(0, this.YPosition - height, pageSizeToImport.Width, height);
directContent.Clip();
directContent.NewPath();
this.Writer.DirectContent.AddTemplate(importedPage, 0, this.YPosition - (finder.VerticalFlips[startFlip] - pageSizeToImport.Bottom));
directContent.RestoreState();
this.YPosition -= height + this.Gap;
startFlip = endFlip - 1;
} else if (!first) {
throw new ArgumentException(string.Format("Page {0} content too large", pageIndex));
} // if
first = false;
} // while
} // Merge
} // PdfVeryDenseMergeTool
The RenderListener class:
UPDATE 3: FIXED 1 LINE OF CODE AND IT WORKS: See comment in code
public class PageVerticalAnalyzer : IRenderListener {
public PageVerticalAnalyzer() { }
public List<float> VerticalFlips = new List<float>();
public void AddVerticalUseSection(float from, float to) {
if (to < from) {
float temp = to;
to = from;
from = temp;
}
int i = 0;
int j = 0;
for (i = 0; i < VerticalFlips.Count; i++) {
float flip = VerticalFlips[i];
if (flip < from)
continue;
for (j = i; j < VerticalFlips.Count; j++) {
flip = VerticalFlips[j];
if (flip < to)
continue;
break;
}
break;
} // foreach of the vertical flips
bool fromOutsideInterval = i % 2 == 0;
bool toOutsideInterval = j % 2 == 0;
while (j-- > i)
VerticalFlips.RemoveAt(j); // This was the problem line with just .Remove(j)
if (toOutsideInterval)
VerticalFlips.Insert(i, to);
if (fromOutsideInterval)
VerticalFlips.Insert(i, from);
} // AddVerticalUseSection
public void BeginTextBlock() { /* Do nothing */ }
public void EndTextBlock() { /* Do nothing */ }
public void RenderImage(ImageRenderInfo renderInfo) {
Matrix ctm = renderInfo.GetImageCTM();
List<float> YCoords = new List<float>(4) { 0, 0, 0, 0 };
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
Vector corner = new Vector(x, y, 1).Cross(ctm);
YCoords[2 * x + y] = corner[Vector.I2];
}
}
YCoords.Sort();
AddVerticalUseSection(YCoords[0], YCoords[3]);
} // RenderImage
public void RenderText(TextRenderInfo renderInfo) {
LineSegment ascentLine = renderInfo.GetAscentLine();
LineSegment descentLine = renderInfo.GetDescentLine();
List<float> YCoords = new List<float>(4) {
ascentLine.GetStartPoint()[Vector.I2],
ascentLine.GetEndPoint()[Vector.I2],
descentLine.GetStartPoint()[Vector.I2],
descentLine.GetEndPoint()[Vector.I2],
};
YCoords.Sort();
AddVerticalUseSection(YCoords[0], YCoords[3]);
} // RenderText
} // PageVericalAnalyzer
Code to gather files and run the tool:
public void TestMergeDocuments() {
PdfVeryDenseMergeTool tool = new PdfVeryDenseMergeTool(iTextSharp.text.PageSize.A4, 18, 18, 10);
List<byte[]> Files = new List<byte[]>();
// Code to load each of the 3 files I need into this byte array list
using (MemoryStream ms = new MemoryStream()) {
List<PdfReader> files = new List<PdfReader>();
foreach (byte[] ba in Files) {
files.Add(new PdfReader(ba));
} // foreach of the sets of data
tool.Merge(ms, files);
// Save the file using: ms.GetBuffer()
} // using the memory stream
} // TestMergeDocuments
The following sample tool has been implemented along the ideas of the tool PdfDenseMergeTool from this answer which the OP has commented to be SO close to what [he] NEEDs. Just like PdfDenseMergeTool this tool here is implemented in Java/iText which I'm more at home with than C#/iTextSharp. As the OP has already translated PdfDenseMergeTool to C#/iTextSharp, translating this tool here also should not be too great a problem.
PdfVeryDenseMergeTool
This tool similarly to PdfDenseMergeTool takes the page contents of pages from a number of PdfReader instances and tries to merge them densely, i.e. putting contents of multiple source pages onto a single target page if there is enough free space to do so. In contrast to that earlier tool, this tool even splits source page contents to allow for an even denser merge.
Just like that other tool the PdfVeryDenseMergeTool does not take vector graphics into account because the iText(Sharp) parsing API does only forward text and bitmap images
The PdfVeryDenseMergeTool splits source pages which do not completely fit onto a target page at a horizontal line which is not intersected by the bounding boxes of text glyphs or bitmap graphics.
The tool class:
public class PdfVeryDenseMergeTool
{
public PdfVeryDenseMergeTool(Rectangle size, float top, float bottom, float gap)
{
this.pageSize = size;
this.topMargin = top;
this.bottomMargin = bottom;
this.gap = gap;
}
public void merge(OutputStream outputStream, Iterable<PdfReader> inputs) throws DocumentException, IOException
{
try
{
openDocument(outputStream);
for (PdfReader reader: inputs)
{
merge(reader);
}
}
finally
{
closeDocument();
}
}
void openDocument(OutputStream outputStream) throws DocumentException
{
final Document document = new Document(pageSize, 36, 36, topMargin, bottomMargin);
final PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
this.document = document;
this.writer = writer;
newPage();
}
void closeDocument()
{
try
{
document.close();
}
finally
{
this.document = null;
this.writer = null;
this.yPosition = 0;
}
}
void newPage()
{
document.newPage();
yPosition = pageSize.getTop(topMargin);
}
void merge(PdfReader reader) throws IOException
{
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
for (int page = 1; page <= reader.getNumberOfPages(); page++)
{
merge(reader, parser, page);
}
}
void merge(PdfReader reader, PdfReaderContentParser parser, int page) throws IOException
{
PdfImportedPage importedPage = writer.getImportedPage(reader, page);
PdfContentByte directContent = writer.getDirectContent();
PageVerticalAnalyzer finder = parser.processContent(page, new PageVerticalAnalyzer());
if (finder.verticalFlips.size() < 2)
return;
Rectangle pageSizeToImport = reader.getPageSize(page);
int startFlip = finder.verticalFlips.size() - 1;
boolean first = true;
while (startFlip > 0)
{
if (!first)
newPage();
float freeSpace = yPosition - pageSize.getBottom(bottomMargin);
int endFlip = startFlip + 1;
while ((endFlip > 1) && (finder.verticalFlips.get(startFlip) - finder.verticalFlips.get(endFlip - 2) < freeSpace))
endFlip -=2;
if (endFlip < startFlip)
{
float height = finder.verticalFlips.get(startFlip) - finder.verticalFlips.get(endFlip);
directContent.saveState();
directContent.rectangle(0, yPosition - height, pageSizeToImport.getWidth(), height);
directContent.clip();
directContent.newPath();
writer.getDirectContent().addTemplate(importedPage, 0, yPosition - (finder.verticalFlips.get(startFlip) - pageSizeToImport.getBottom()));
directContent.restoreState();
yPosition -= height + gap;
startFlip = endFlip - 1;
}
else if (!first)
throw new IllegalArgumentException(String.format("Page %s content sections too large.", page));
first = false;
}
}
Document document = null;
PdfWriter writer = null;
float yPosition = 0;
final Rectangle pageSize;
final float topMargin;
final float bottomMargin;
final float gap;
}
(PdfVeryDenseMergeTool.java)
This tool makes use of a custom RenderListener for use with the iText parser API:
public class PageVerticalAnalyzer implements RenderListener
{
#Override
public void beginTextBlock() { }
#Override
public void endTextBlock() { }
/*
* #see RenderListener#renderText(TextRenderInfo)
*/
#Override
public void renderText(TextRenderInfo renderInfo)
{
LineSegment ascentLine = renderInfo.getAscentLine();
LineSegment descentLine = renderInfo.getDescentLine();
float[] yCoords = new float[]{
ascentLine.getStartPoint().get(Vector.I2),
ascentLine.getEndPoint().get(Vector.I2),
descentLine.getStartPoint().get(Vector.I2),
descentLine.getEndPoint().get(Vector.I2)
};
Arrays.sort(yCoords);
addVerticalUseSection(yCoords[0], yCoords[3]);
}
/*
* #see RenderListener#renderImage(ImageRenderInfo)
*/
#Override
public void renderImage(ImageRenderInfo renderInfo)
{
Matrix ctm = renderInfo.getImageCTM();
float[] yCoords = new float[4];
for (int x=0; x < 2; x++)
for (int y=0; y < 2; y++)
{
Vector corner = new Vector(x, y, 1).cross(ctm);
yCoords[2*x+y] = corner.get(Vector.I2);
}
Arrays.sort(yCoords);
addVerticalUseSection(yCoords[0], yCoords[3]);
}
/**
* This method marks the given interval as used.
*/
void addVerticalUseSection(float from, float to)
{
if (to < from)
{
float temp = to;
to = from;
from = temp;
}
int i=0, j=0;
for (; i<verticalFlips.size(); i++)
{
float flip = verticalFlips.get(i);
if (flip < from)
continue;
for (j=i; j<verticalFlips.size(); j++)
{
flip = verticalFlips.get(j);
if (flip < to)
continue;
break;
}
break;
}
boolean fromOutsideInterval = i%2==0;
boolean toOutsideInterval = j%2==0;
while (j-- > i)
verticalFlips.remove(j);
if (toOutsideInterval)
verticalFlips.add(i, to);
if (fromOutsideInterval)
verticalFlips.add(i, from);
}
final List<Float> verticalFlips = new ArrayList<Float>();
}
(PageVerticalAnalyzer.java)
It is used like this:
PdfVeryDenseMergeTool tool = new PdfVeryDenseMergeTool(PageSize.A4, 18, 18, 5);
tool.merge(output, inputs);
(VeryDenseMerging.java)
Applied to the OP's sample documents
Header.pdf
Body.pdf
Footer.pdf
it generates
If one defines the target document page size to be A5 landscape:
PdfVeryDenseMergeTool tool = new PdfVeryDenseMergeTool(new RectangleReadOnly(595,421), 18, 18, 5);
tool.merge(output, inputs);
(VeryDenseMerging.java)
it generates this:
Beware! This is only a proof of concept and it does not consider all possibilities. E.g. the case of source or target pages with a non-trivial Rotate value is not properly handled. Thus, it is not ready for production use yet.
Improvement in current (5.5.6 SNAPSHOT) iText version
The current iText development version towards 5.5.6 enhances the parser functionality to also signal vector graphics. Thus, I extended the PageVerticalAnalyzer to make use of this:
public class PageVerticalAnalyzer implements ExtRenderListener
{
#Override
public void beginTextBlock() { }
#Override
public void endTextBlock() { }
#Override
public void clipPath(int rule) { }
...
static class SubPathSection
{
public SubPathSection(float x, float y, Matrix m)
{
float effectiveY = getTransformedY(x, y, m);
pathFromY = effectiveY;
pathToY = effectiveY;
}
void extendTo(float x, float y, Matrix m)
{
float effectiveY = getTransformedY(x, y, m);
if (effectiveY < pathFromY)
pathFromY = effectiveY;
else if (effectiveY > pathToY)
pathToY = effectiveY;
}
float getTransformedY(float x, float y, Matrix m)
{
return new Vector(x, y, 1).cross(m).get(Vector.I2);
}
float getFromY()
{
return pathFromY;
}
float getToY()
{
return pathToY;
}
private float pathFromY;
private float pathToY;
}
/*
* Beware: The implementation is not correct as it includes the control points of curves
* which may be far outside the actual curve.
*
* #see ExtRenderListener#modifyPath(PathConstructionRenderInfo)
*/
#Override
public void modifyPath(PathConstructionRenderInfo renderInfo)
{
Matrix ctm = renderInfo.getCtm();
List<Float> segmentData = renderInfo.getSegmentData();
switch (renderInfo.getOperation())
{
case PathConstructionRenderInfo.MOVETO:
subPath = null;
case PathConstructionRenderInfo.LINETO:
case PathConstructionRenderInfo.CURVE_123:
case PathConstructionRenderInfo.CURVE_13:
case PathConstructionRenderInfo.CURVE_23:
for (int i = 0; i < segmentData.size()-1; i+=2)
{
if (subPath == null)
{
subPath = new SubPathSection(segmentData.get(i), segmentData.get(i+1), ctm);
path.add(subPath);
}
else
subPath.extendTo(segmentData.get(i), segmentData.get(i+1), ctm);
}
break;
case PathConstructionRenderInfo.RECT:
float x = segmentData.get(0);
float y = segmentData.get(1);
float w = segmentData.get(2);
float h = segmentData.get(3);
SubPathSection section = new SubPathSection(x, y, ctm);
section.extendTo(x+w, y, ctm);
section.extendTo(x, y+h, ctm);
section.extendTo(x+w, y+h, ctm);
path.add(section);
case PathConstructionRenderInfo.CLOSE:
subPath = null;
break;
default:
}
}
/*
* #see ExtRenderListener#renderPath(PathPaintingRenderInfo)
*/
#Override
public Path renderPath(PathPaintingRenderInfo renderInfo)
{
if (renderInfo.getOperation() != PathPaintingRenderInfo.NO_OP)
{
for (SubPathSection section : path)
addVerticalUseSection(section.getFromY(), section.getToY());
}
path.clear();
subPath = null;
return null;
}
List<SubPathSection> path = new ArrayList<SubPathSection>();
SubPathSection subPath = null;
...
}
(PageVerticalAnalyzer.java)
A simple test (VeryDenseMerging.java method testMergeOnlyGraphics) merges these files
into this:
But once again beware: this is a mere proof of concept. Especially modifyPath() needs to be improved, the implementation is not correct as it includes the control points of curves which may be far outside the actual curve.
I've been wanting to design a generator for dragon curves.
(If you want info on that check this out, but it doesn't really matter for the issue)
A dragon curve is a repeating mathematical construct.
I've already written a generator for what the canvas should draw, it works by returning a char array consisting of 'r' or 'l', saying whether the line has to turn left or right next. In the code here, it's the method input(). This part works perfectly.
The problem is that whenever I want to draw it on the canvas (using drawLine), it only draws the first two lines as actual lines, the rest are just dots.
The dots are on the right positions and if you make the thing really big, you can't tell the difference anymore, but nevertheless, there are supposed to be lines there.
Image:
This is the code I used:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
*
* Description
*
* #version 1.0 from 4/20/2016
* #author
*/
public class CurveGen extends JFrame {
// start attributes
private Canvas display = new Canvas();
private JButton startButton = new JButton();
private JLabel jLabel1 = new JLabel();
private JTextArea outText = new JTextArea("");
private JScrollPane outTextScrollPane = new JScrollPane(outText);
private JLabel jLabel2 = new JLabel();
private JSlider xSlider = new JSlider();
private JSlider ySlider = new JSlider();
private JNumberField iterationsNF = new JNumberField();
private JNumberField sizeNF = new JNumberField();
// end attributes
public CurveGen(String title) {
// Frame-Init
super(title);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
int frameWidth = 1022;
int frameHeight = 731;
setSize(frameWidth, frameHeight);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int x = (d.width - getSize().width) / 2;
int y = (d.height - getSize().height) / 2;
setLocation(x, y);
setResizable(false);
Container cp = getContentPane();
cp.setLayout(null);
// start components
display.setBounds(16, 64, 601, 601);
cp.add(display);
startButton.setBounds(736, 464, 241, 129);
startButton.setText("START!");
startButton.setMargin(new Insets(2, 2, 2, 2));
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
startButton_ActionPerformed(evt);
}
});
startButton.setFont(new Font("Dialog", Font.BOLD, 36));
cp.add(startButton);
jLabel1.setBounds(760, 96, 75, 41);
jLabel1.setText("Iterations:");
cp.add(jLabel1);
outTextScrollPane.setBounds(728, 392, 257, 57);
cp.add(outTextScrollPane);
jLabel2.setBounds(768, 144, 67, 41);
jLabel2.setText("Size:");
cp.add(jLabel2);
xSlider.setBounds(0, 8, 633, 49);
xSlider.setMinorTickSpacing(25);
xSlider.setMajorTickSpacing(100);
xSlider.setPaintTicks(true);
xSlider.setPaintLabels(true);
xSlider.setToolTipText("Starting point y-coordinate");
xSlider.setMaximum(600);
xSlider.setValue(300);
cp.add(xSlider);
ySlider.setBounds(624, 56, 65, 625);
ySlider.setMinorTickSpacing(25);
ySlider.setMajorTickSpacing(100);
ySlider.setPaintTicks(true);
ySlider.setPaintLabels(true);
ySlider.setOrientation(SwingConstants.VERTICAL);
ySlider.setMaximum(600);
ySlider.setInverted(true);
ySlider.setValue(300);
ySlider.setToolTipText("Starting point x-coordinate");
cp.add(ySlider);
iterationsNF.setBounds(856, 96, 81, 41);
iterationsNF.setText("");
cp.add(iterationsNF);
sizeNF.setBounds(856, 144, 81, 41);
sizeNF.setText("");
cp.add(sizeNF);
// end components
setVisible(true);
} // end of public CurveGen
// start methods
public static void main(String[] args) {
new CurveGen("CurveGen");
} // end of main
public char[] input(int iter) {
char oldOut[] = new char[0];
for (int i=1;i<=iter;i++) {
char newOut[] = new char[((int)Math.pow(2, i))-1];
for (int n=0;n<oldOut.length;n++) {
newOut[n] = oldOut[n];
if (oldOut[n]=='r') {
newOut[newOut.length-n-1] = 'l';
}
if (oldOut[n]=='l') {
newOut[newOut.length-n-1] = 'r';
} // end of if
} // end of for
newOut[oldOut.length]='l';
oldOut = newOut;
} // end of for
return oldOut;
}
public void startButton_ActionPerformed(ActionEvent evt) {
int iterations = iterationsNF.getInt();
int size = sizeNF.getInt();
char com[] = input(iterations);
outText.setText(String.valueOf(com));
int dir = 0;
int newDir = 0;
int lastPos[] = {xSlider.getValue(),ySlider.getValue()-size};
int newPos[] = {0,0};
Graphics g = display.getGraphics();
g.clearRect(0,0,601,601);
g.drawLine(xSlider.getValue(),ySlider.getValue(),xSlider.getValue(),ySlider.getValue()-size);
for (int i=0;i<=com.length-1;i++) {
dir = newDir;
if (dir==0) {
if (com[i]=='l') {
newPos[0] = lastPos[0]-size;
newPos[1] = lastPos[1];
newDir = 3;
}
if (com[i]=='r') {
newPos[0] = lastPos[0]+size;
newPos[1] = lastPos[1];
newDir = 1;
}
}
if (dir==1) {
if (com[i]=='l') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]-size;
newDir = 0;
}
if (com[i]=='r') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]+size;
newDir = 2;
}
}
if (dir==2) {
if (com[i]=='l') {
newPos[0] = lastPos[0]+size;
newPos[1] = lastPos[1];
newDir = 1;
}
if (com[i]=='r') {
newPos[0] = lastPos[0]-size;
newPos[1] = lastPos[1];
newDir = 3;
}
}
if (dir==3) {
if (com[i]=='l') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]+size;
newDir = 2;
}
if (com[i]=='r') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]-size;
newDir = 0;
}
}
g.drawLine(lastPos[0],lastPos[1],newPos[0],newPos[1]);
lastPos=newPos;
} // end of for
} // end of startButton_ActionPerformed
// end methods
} // end of class CurveGen
Okay, so I've gone back over the code...
Mixing heavyweight (java.awt.Canvas) and lightweight (Swing) components is unadvisable as they can cause or sorts of painting issues
getGraphics is not how paint should be done. Instead, I'd start with a custom JPanel and override its paintComponent. See Painting in AWT and Swing and Performing Custom Painting for more details
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
I believe the problem is associated with this...
lastPos=newPos;
All you are doing is making lastPos point to the same place in memory as newPos, so when you assign values to newPos, lastPos will have the same values, hence the reason you're seeing dots.
What I would do first, is separate the responsible for the generation of the data from the display.
I'd start with some kind of model (note, you could create a model which took iterations instead and which generated the data itself, but I was focusing on solving the initial problem)
public class DragonModel {
private Point startPoint;
private int size;
private char[] values;
public DragonModel(Point startPoint, int size, char[] values) {
this.startPoint = startPoint;
this.size = size;
this.values = values;
}
public Point getStartPoint() {
return startPoint;
}
public int getSize() {
return size;
}
public char[] getValues() {
return values;
}
}
and then the display...
public class DragonPane extends JPanel {
private DragonModel model;
public void setModel(DragonModel model) {
this.model = model;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (model != null) {
Graphics2D g2d = (Graphics2D) g.create();
int size = model.getSize();
int dir = 0;
int newDir = 0;
Point lastPos = model.getStartPoint();
Point newPos = new Point(0, 0);
for (char value : model.values) {
if (dir == 0) {
if (value == 'l') {
newPos.x = lastPos.x - size;
newPos.y = lastPos.y;
newDir = 3;
}
if (value == 'r') {
newPos.x = lastPos.x + size;
newPos.y = lastPos.y;
newDir = 1;
}
}
if (dir == 1) {
if (value == 'l') {
newPos.x = lastPos.x;
newPos.y = lastPos.y - size;
newDir = 0;
}
if (value == 'r') {
newPos.x = lastPos.x;
newPos.y = lastPos.y + size;
newDir = 2;
}
}
if (dir == 2) {
if (value == 'l') {
newPos.x = lastPos.x + size;
newPos.y = lastPos.y;
newDir = 1;
}
if (value == 'r') {
newPos.x = lastPos.x - size;
newPos.y = lastPos.y;
newDir = 3;
}
}
if (dir == 3) {
if (value == 'l') {
newPos.x = lastPos.x;
newPos.y = lastPos.y + size;
newDir = 2;
}
if (value == 'r') {
newPos.x = lastPos.x;
newPos.y = lastPos.y - size;
newDir = 0;
}
}
g.drawLine(lastPos.x, lastPos.y, newPos.x, newPos.y);
dir = newDir;
lastPos = new Point(newPos);
}
}
}
}
The idea here is to try and decouple of the responsibility a little, the responsibility for the generation and displaying of the data sit firmly in two different areas.
Then in your actionPerformed method you could simply do...
public void startButton_ActionPerformed(ActionEvent evt) {
int iterations = Integer.parseInt(iterationsNF.getText());
int size = Integer.parseInt(sizeNF.getText());
char com[] = input(iterations);
outText.setText(String.valueOf(com));
DragonModel model = new DragonModel(new Point(xSlider.getValue(), ySlider.getValue()), size, com);
display.setModel(model);
} // end of startButton_ActionPerformed
which could result in something like...
The drawing code should be inside the paint(Graphics) method to synchronize with the rendering loop properly. In an event handler, update the data model of the component (calculate the lines and keep them in a data structure inside the component), then call the method repaint() to trigger the event rendering loop, which will call your paint method.
There are some other variations of this, but the general idea is that you change the data and then request rendering. The rendering engine may call your paint method in other cases as well, not only when you change the data, so ideally paint() has all the data it needs to render fast, meaning it should not do calculations or heavy operations other than rendering on the Graphics object.
This means that you have to subclass JComponent in a new class, and implement paint inside it. This class should have an internal data structure with the lines ready to render at any point in time. Then use your new class in the JFrame.
What are the printer commands used for TVS LP 45 barcode printer ? We used TSCLIB.dll file to get our java project to print , but it doesn't print ? What is the actual barcode printer commands used to print TVS LP 45 ?
Some months ago i worked with LP 45 label printer. I used the regular Java code for printing.
package default;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import static java.awt.print.Printable.NO_SUCH_PAGE;
import static java.awt.print.Printable.PAGE_EXISTS;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.imageio.ImageIO;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
/**
*
* #author All Open source developers
* #version 1.0.0.0
* #since 2014/12/22
*/
/*This Printsupport java class was implemented to get printout.
* This class was specially designed to print a Jtable content to a paper.
* Specially this class formated to print 7cm width paper.
* Generally for pos thermel printer.
* Free to customize this source code as you want.
* Illustration of basic invoice is in this code.
* demo by gayan liyanaarachchi
*/
public class Printsupport {
static JTable itemsTable;
public static int total_item_count=0;
public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss a";
public static String title[] = new String[] {"Item ID","Item Name","Price","Quantity"};
public static void setItems(Object[][] printitem){
Object data[][]=printitem;
DefaultTableModel model = new DefaultTableModel();
//assume jtable has 4 columns.
model.addColumn(title[0]);
model.addColumn(title[1]);
model.addColumn(title[2]);
model.addColumn(title[3]);
int rowcount=printitem.length;
addtomodel(model, data, rowcount);
itemsTable = new JTable(model);
}
public static void addtomodel(DefaultTableModel model,Object [][]data,int rowcount){
int count=0;
while(count < rowcount){
model.addRow(data[count]);
count++;
}
if(model.getRowCount()!=rowcount)
addtomodel(model, data, rowcount);
System.out.println("Check Passed.");
}
public Object[][] getTableData (JTable table) {
int itemcount=table.getRowCount();
System.out.println("Item Count:"+itemcount);
DefaultTableModel dtm = (DefaultTableModel) table.getModel();
int nRow = dtm.getRowCount(), nCol =dtm.getColumnCount();
Object[][] tableData = new Object[nRow][nCol];
if(itemcount==nRow) //check is there any data loss.
{
for (int i = 0 ; i < nRow ; i++){
for (int j = 0 ; j < nCol ; j++){
tableData[i][j] = dtm.getValueAt(i,j); //pass data into object array.
}}
if(tableData.length!=itemcount){ //check for data losses in object array
getTableData(table); //recursively call method back to collect data
}
System.out.println("Data check passed");
}
else{
//collecting data again because of data loss.
getTableData(table);
}
return tableData; //return object array with data.
}
public static PageFormat getPageFormat(PrinterJob pj){
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
double middleHeight =total_item_count*1.0; //dynamic----->change with the row count of jtable
double headerHeight = 5.0; //fixed----->but can be mod
double footerHeight = 5.0; //fixed----->but can be mod
double width = convert_CM_To_PPI(7); //printer know only point per inch.default value is 72ppi
double height = convert_CM_To_PPI(headerHeight+middleHeight+footerHeight);
paper.setSize(width, height);
paper.setImageableArea(
convert_CM_To_PPI(0.25),
convert_CM_To_PPI(0.5),
width - convert_CM_To_PPI(0.35),
height - convert_CM_To_PPI(1)); //define boarder size after that print area width is about 180 points
pf.setOrientation(PageFormat.PORTRAIT); //select orientation portrait or landscape but for this time portrait
pf.setPaper(paper);
return pf;
}
protected static double convert_CM_To_PPI(double cm) {
return toPPI(cm * 0.393600787);
}
protected static double toPPI(double inch) {
return inch * 72d;
}
public static String now() {
//get current date and time as a String output
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
return sdf.format(cal.getTime());
}
public static class MyPrintable implements Printable {
#Override
public int print(Graphics graphics, PageFormat pageFormat,
int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
if (pageIndex == 0) {
Graphics2D g2d = (Graphics2D) graphics;
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
g2d.translate((int) pageFormat.getImageableX(),(int) pageFormat.getImageableY());
Font font = new Font("Monospaced",Font.PLAIN,7);
g2d.setFont(font);
try {
/*
* Draw Image*
assume that printing reciept has logo on top
* that logo image is in .gif format .png also support
* image resolution is width 100px and height 50px
* image located in root--->image folder
*/
int x=100 ; //print start at 100 on x axies
int y=10; //print start at 10 on y axies
int imagewidth=100;
int imageheight=50;
BufferedImage read = ImageIO.read(getClass().getResource("/image/logo.gif"));
g2d.drawImage(read,x,y,imagewidth,imageheight,null); //draw image
g2d.drawLine(10, y+60, 180, y+60); //draw line
} catch (IOException e) {
e.printStackTrace();
}
try{
/*Draw Header*/
int y=80;
g2d.drawString("ABC Shopping Complex", 40,y);
g2d.drawString("CopyWrite 2009-2014", 50,y+10); //shift a line by adding 10 to y value
g2d.drawString(now(), 10, y+20); //print date
g2d.drawString("Cashier : admin", 10, y+30);
/*Draw Colums*/
g2d.drawLine(10, y+40, 180, y+40);
g2d.drawString(title[0], 10 ,y+50);
g2d.drawString(title[1], 50 ,y+50);
g2d.drawString(title[2], 100 ,y+50);
g2d.drawString(title[3], 150 ,y+50);
g2d.drawLine(10, y+60, 180, y+60);
int cH = 0;
TableModel mod = itemsTable.getModel();
for(int i = 0;i < mod.getRowCount() ; i++){
/*Assume that all parameters are in string data type for this situation
* All other premetive data types are accepted.
*/
String itemid = mod.getValueAt(i, 0).toString();
String itemname = mod.getValueAt(i, 1).toString();
String price = mod.getValueAt(i, 2).toString();
String quantity = mod.getValueAt(i, 3).toString();
cH = (y+70) + (10*i); //shifting drawing line
g2d.drawString(itemid, 0, cH);
g2d.drawString(itemname,50, cH);
g2d.drawString(price , 100, cH);
g2d.drawString(quantity , 150, cH);
}
/*Footer*/
font = new Font("Arial",Font.BOLD,16) ; //changed font size
g2d.setFont(font);
g2d.drawString("Thank You Come Again",30, cH+10);
//end of the reciept
}
catch(Exception r){
r.printStackTrace();
}
result = PAGE_EXISTS;
}
return result;
}
}
}
/*
################# THIS IS HOW TO USE THIS CLASS #######################
Printsupport ps=new Printsupport();
Object printitem [][]=ps.getTableData(jTable);
ps.setItems(printitem);
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(new MyPrintable(),ps.getPageFormat(pj));
try {
pj.print();
}
catch (PrinterException ex) {
ex.printStackTrace();
}
################## JOIN TO SHARE KNOWLADGE ###########################
*/
Sometimes there arise a conflict between Java API with the printer driver. I solve it by deleting all the default paper templates in driver interface.
In the CalendarCanvas class, the idea is to press on any day in the Calendar and a form will be presented with the actual date of the day THE USER picked (not just today's date as it's doing now) and underneath the form will be the image pertaining to the form (eg. June 21st (image.jpg)).
I'm having two problems.
Number one, the program only likes one image which is the Men.img. It will display that image in all of the three forms. The other two images will not display at all.
And I don't understand because I first started with one form and the images.jpg image and that image worked. All I did was create 2 new forms and switch the order of images.
I even reduced the sizes (width/height & KB) to match with Men.jpg but it still doesn't show. The string inside the ImageItem constructor doesn't even show.
Lastly how do I create a new instance of the calendar.getSelectedDate().toString() method to show the new user picked date so I can put that above the image when the user picks a different day?
In the keyPressed method, the commented out new instance of Alert will do that for me but I don't want an alert, I want it to be the title of the forms pertaining to what day the user picked.
Below is my code:
Class CalendarMidlet:
import java.io.IOException;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
* #author addylesson
*/
public class CalendarMidlet extends MIDlet {
private Display mydisplay;
private Command f;
private Displayable d;
public CalendarMidlet(){}
public void startApp() {
d = new CalendarCanvas(this);
f = new Command("Exit", Command.EXIT, 0);
d.addCommand(f);
d.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if(c == f)
notifyDestroyed();
}
} );
mydisplay = Display.getDisplay(this);
mydisplay.setCurrent(d);
}
public void startOver() {
final Displayable d = new CalendarCanvas(this);
d.addCommand(f);
d.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if(c == f)
notifyDestroyed();
}
} );
mydisplay.setCurrent(d);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
Class CalendarCanvas:
import java.io.IOException;
import java.util.Date;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class CalendarCanvas extends Canvas
{
CalendarWidget calendar = null;
CalendarMidlet midlet = null;
private Alert alert;
private List mList;
private Command f,e,n;
private Display mydisplay;
private Form form;
private Form form2;
private Form form3;
private ImageItem imageItem;
private ImageItem imageItem2;
private ImageItem imageItem3;
public CalendarCanvas(final CalendarMidlet m)
{
String day = calendar.getSelectedDate().toString();
this.midlet = m;
calendar = new CalendarWidget(new Date());
calendar.headerFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE);
calendar.weekdayFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
calendar.weekdayBgColor = 0xccccff;
calendar.weekdayColor = 0x0000ff;
calendar.headerColor = 0xffffff;
form = new Form(day);
form2 = new Form("New Page");
form3 = new Form("New Page");
try
{
imageItem = new ImageItem(
"Men In Black 3: ",
Image.createImage("Men.jpg"),
ImageItem.LAYOUT_DEFAULT,
"DuKe");
form.append(imageItem);
} catch(Exception s) {}
try
{
imageItem2 = new ImageItem(
"Jordon vs Knicks: ",
Image.createImage("images.jpg"),
ImageItem.LAYOUT_DEFAULT,
"Not");
form2.append(imageItem2);
} catch(Exception s) {}
try
{
imageItem3 = new ImageItem(
"Horoscope: ",
Image.createImage("Men.jpg"),
ImageItem.LAYOUT_DEFAULT,
"DuKe");
form3.append(imageItem3);
} catch(Exception s) {}
alert = new Alert("Listen", "On this day "
+calendar.getSelectedDate().toString()+ "stuff happened", null, null);
alert.setTimeout(Alert.FOREVER);
n = new Command("Next", Command.SCREEN,0);
f = new Command("Back", Command.BACK, 0);
e = new Command("Exit", Command.EXIT, 1);
form.addCommand(f);
form.addCommand(n);
form2.addCommand(f);
form2.addCommand(n);
form3.addCommand(f);
form3.addCommand(e);
form.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if (c == f)
m.startOver();
if (c == e)
m.notifyDestroyed();
if (c == n)
Display.getDisplay(midlet).setCurrent(form2);
}
} );
form2.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if (c == f)
m.startOver();
if (c == e)
m.notifyDestroyed();
if (c == n)
Display.getDisplay(midlet).setCurrent(form3);
}
} );
form3.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if (c == f)
m.startOver();
if (c == e)
m.notifyDestroyed();
}
} );
calendar.initialize();
}
protected void keyPressed(int key)
{
int keyCode = getGameAction(key);
String day = calendar.getSelectedDate().toString();
if(keyCode == FIRE)
{
/*Display.getDisplay(midlet).setCurrent(
new Alert("Selected date",
day, null,
AlertType.CONFIRMATION)
);*/
Display.getDisplay(midlet).setCurrent(form);
}
else
{
calendar.keyPressed(keyCode);
repaint();
}
}
protected void paint(Graphics g)
{
g.setColor(0xff0000);
g.fillRect(0, 0, getWidth(), getHeight());
calendar.paint(g);
}
}
Class CalendarWidget:
import java.util.Calendar;
import java.util.Date;
import javax.microedition.lcdui.*;
public class CalendarWidget
{
static final String[] MONTH_LABELS = new String[]{
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
};
static final String[] WEEKDAY_LABELS = new String[]{
"M", "T", "W", "T", "F", "S", "S"
};
/* starting week day: 0 for monday, 6 for sunday */
public int startWeekday = 0;
/* elements padding */
public int padding = 2;
/* cells border properties */
public int borderWidth = 4;
public int borderColor = 0x000000;
/* weekday labels properties */
public Font weekdayFont = Font.getDefaultFont();
public int weekdayBgColor = 0xcccccc;
public int weekdayColor = 0xffffff;
/* header (month-year label) properties */
public Font headerFont = Font.getDefaultFont();
public int headerBgColor = 0xff0000;
public int headerColor = 0xff0000;
/* cells properties */
public Font font = Font.getDefaultFont();
public int foreColor = 0xffffff;
public int bgColor = 0xcccccc;
public int selectedBgColor = 0x000000;
public int selectedForeColor = 0xff0000;
/* internal properties */
int width = 0;
int height = 0;
int headerHeight = 0;
int weekHeight = 0;
int cellWidth = 0;
int cellHeight = 0;
/* internal time properties */
long currentTimestamp = 0;
Calendar calendar = null;
int weeks = 0;
public CalendarWidget(Date date)
{
calendar = Calendar.getInstance();
//we'll see these 2 methods later
setDate(date);
initialize();
}
public Date getSelectedDate()
{
return calendar.getTime();
}
public void setDate(Date d)
{
currentTimestamp = d.getTime();
calendar.setTime(d);
//weeks number can change, depending on week starting day and month total days
this.weeks = (int)Math.ceil(((double)getStartWeekday() + getMonthDays()) / 7);
}
public void setDate(long timestamp)
{
setDate(new Date(timestamp));
}
void initialize()
{
//let's initialize calendar size
this.cellWidth = font.stringWidth("MM") + 3 * padding;
this.cellHeight = font.getHeight() + 15 * padding;
this.headerHeight = headerFont.getHeight() + 2 * padding;
this.weekHeight = weekdayFont.getHeight() + 2 * padding;
this.width = 7 * (cellWidth + borderWidth) + borderWidth;
initHeight();
}
void initHeight()
{
this.height =
headerHeight + weekHeight +
this.weeks * (cellHeight + borderWidth) + borderWidth;
}
int getMonthDays()
{
int month = calendar.get(Calendar.MONTH);
switch(month)
{
case 3:
case 5:
case 8:
case 10:
return 30;
case 1:
return calendar.get(Calendar.YEAR) % 4 == 0 && calendar.get(Calendar.YEAR) % 100 != 0 ? 29 : 28;
default:
return 31;
}
}
int getStartWeekday()
{
//let's create a new calendar with same month and year, but with day 1
Calendar c = Calendar.getInstance();
c.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
c.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
c.set(Calendar.DAY_OF_MONTH, 1);
//we must normalize DAY_OF_WEEK returned value
return (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
}
public void keyPressed(int key)
{
switch(key)
{
case Canvas.UP:
go(-7);
break;
case Canvas.DOWN:
go(7);
break;
case Canvas.RIGHT:
go(1);
break;
case Canvas.LEFT:
go(-1);
break;
}
}
void go(int delta)
{
int prevMonth = calendar.get(Calendar.MONTH);
setDate(currentTimestamp + 86400000 * delta);
//we have to check if month has changed
//if yes, we have to recalculate month height
//since weeks number could be changed
if(calendar.get(Calendar.MONTH) != prevMonth)
{
initHeight();
}
}
public void paint(Graphics g)
{
//painting background
g.setColor(bgColor);
g.fillRect(0, 0, width, height);
//painting header (month-year label)
g.setFont(headerFont);
g.setColor(headerColor);
g.drawString(MONTH_LABELS[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.YEAR), width / 2, padding, Graphics.TOP | Graphics.HCENTER);
//painting week days labels
g.translate(0, headerHeight);
g.setColor(weekdayBgColor);
g.fillRect(0, 0, width, weekHeight);
g.setColor(weekdayColor);
g.setFont(weekdayFont);
for(int i = 0; i < 7; i++)
{
g.drawString(WEEKDAY_LABELS[(i + startWeekday) % 7],
borderWidth + i * (cellWidth + borderWidth) + cellWidth / 2,
padding,
Graphics.TOP | Graphics.HCENTER
);
}
g.translate(0, weekHeight);
g.setColor(borderColor);
for(int i = 0; i <= weeks; i++)
{
g.fillRect(0, i * (cellHeight + borderWidth), width, borderWidth);
}
for(int i = 0; i <= 7; i++)
{
g.fillRect(i * (cellWidth + borderWidth), 0, borderWidth, height - headerHeight - weekHeight);
}
int days = getMonthDays();
int dayIndex = (getStartWeekday() - this.startWeekday + 7) % 7;
g.setColor(foreColor);
int currentDay = calendar.get(Calendar.DAY_OF_MONTH);
for(int i = 0; i < days; i++)
{
int weekday = (dayIndex + i) % 7;
int row = (dayIndex + i) / 7;
int x = borderWidth + weekday * (cellWidth + borderWidth) + cellWidth / 2;
int y = borderWidth + row * (cellHeight + borderWidth) + padding;
if(i + 1 == currentDay)
{
g.setColor(selectedBgColor);
g.fillRect(
borderWidth + weekday * (cellWidth + borderWidth),
borderWidth + row * (cellHeight + borderWidth),
cellWidth, cellHeight);
g.setColor(selectedForeColor);
}
g.drawString("" + (i + 1), x, y, Graphics.TOP | Graphics.HCENTER);
if(i + 1 == currentDay)
{
g.setColor(foreColor);
}
}
g.translate(0, - headerHeight - weekHeight);
}
}
There are no evident mistakes in the code snippet you posted.
To easier find out what could possibly go wrong when your MIDlet becomes complicated like that, add logging statements and run it in emulator watching console output.
You definitely need to add logging within catch blocks. To find out why I am making such a strong statement, search Web for something like Java swallow exceptions.
Other points where I'd recommend logging are within commandAction and when invoking setCurrent. When developing, feel free to add more where you feel the need to (keyPressed looks like a good candidate for that, too).
public class Log {
// utility class to keep logging code in one place
public static void log (String message) {
System.out.println(message);
// when debugging at real device, S.o.p above can be refactored
// - based on ideas like one used here (with Form.append):
// http://stackoverflow.com/questions/10649974
// - Another option would be to write log to RMS
// and use dedicated MIDlet to read it from there
// - If MIDlet has network connection, an option is
// to pass log messages over the network. Etc etc...
}
}
// ... other classes...
// ...
catch (Exception e) {
Log.log("unexpected exception: [" + e + "]");
}
// ...
public void commandAction(Command c, Displayable s) {
Log.log("command: [" + c.getCommandLabel()
+ "] at screen: [" + d.getTitle() + "]");
// ...
}
// ...
Log.log("set current: [" + someDisplayable.getTitle() + "]");
mydisplay.setCurrent(someDisplayable);
// ...
protected void keyPressed(int key) {
Log.log("key pressed: [" + getKeyName(key) + "]");
// ...
}