Print bitmap full page width using ESC/POS - java

I am currently implementing Android PrintService, that is able to print PDFs via thermal printers. I managed to convert PDF to bitmap using PDFRenderer and I am even able to print the document.
The thing is, the document (bitmap) is not full page width.
I am receiving the document in 297x420 resolution and I am using printer with 58mm paper.
This is how I process the document (written in C#, using Xamarin):
// Create PDF renderer
var pdfRenderer = new PdfRenderer(fileDescriptor);
// Open page
PdfRenderer.Page page = pdfRenderer.OpenPage(index);
// Create bitmap for page
Bitmap bitmap = Bitmap.CreateBitmap(page.Width, page.Height, Bitmap.Config.Argb8888);
// Now render page into bitmap
page.Render(bitmap, null, null, PdfRenderMode.ForPrint);
And then, converting the bitmap into ESC/POS:
// Initialize result
List<byte> result = new List<byte>();
// Init ESC/POS
result.AddRange(new byte[] { 0x1B, 0x33, 0x21 });
// Init ESC/POS bmp commands (will be reapeated)
byte[] escBmp = new byte[] { 0x1B, 0x2A, 0x01, (byte)(bitmap.Width % 256), (byte)(bitmap.Height / 256) };
// Iterate height
for (int i = 0; i < (bitmap.Height / 24 + 1); i++)
{
// Add bitmapp commands to result
result.AddRange(escBmp);
// Init pixel color
int pixelColor;
// Iterate width
for (int j = 0; j < bitmap.Width; j++)
{
// Init data
byte[] data = new byte[] { 0x00, 0x00, 0x00 };
for (int k = 0; k < 24; k++)
{
if (((i * 24) + k) < bitmap.Height)
{
// Get pixel color
pixelColor = bitmap.GetPixel(j, (i * 24) + k);
// Check pixel color
if (pixelColor != 0)
{
data[k / 8] += (byte)(128 >> (k % 8));
}
}
}
// Add data to result
result.AddRange(data);
}
// Add some... other stuff
result.AddRange(new byte[] { 0x0D, 0x0A });
}
// Return data
return result.ToArray();
Current result looks like this:
Thank you all in advance.

There is no magic "scale-to-page-width" command in the ESC/POS command-set, you need to know the max width of your printer, available in the manual, and then you can:
Double the width and height for some image output commands -- You are using ESC *, which supports low-density, but height and width change in different ratios.
Render the PDF wider to begin with - match the Bitmap size to the printer page width, and not the PDF page width. The same problem is solved at PDFrenderer setting scale to screen
You can also simply stretch the image before you send it, if you are happy with the low quality. See: How to Resize a Bitmap in Android?
Aside, your ESC * implementation is incorrect. There are two bytes for the width- Check the ESC/POS manual for the correct usage, or read over the correct implementations in PHP or Python that I've linked in another question: ESC POS command ESC* for printing bit image on printer

Related

Handling large images

I am building an app on which I to deal with extra large images like of 10000X5000 pixels.The app crashes when I tried to display such images on ImageView.
I can't go for bitmap sampling as I can't change the size of images.
A simple work is to select an image of 10000X5000 pixels and display it in an ImageView.
Currently, I am using simple code for this purpose and app is crashing on it.
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
// Get the cursor
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imgDecodableString = cursor.getString(columnIndex);
cursor.close();
ImageView imgView = (ImageView) findViewById(R.id.imgView);
// Set the Image in ImageView after decoding the String
imgView.setImageBitmap(BitmapFactory
.decodeFile(imgDecodableString));
The Exception is of OutOfMemory
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.star.largeiamgesdemo,
PID: 21155
java.lang.OutOfMemoryError: Failed to allocate a 200000012 byte
allocation with 4194208 free bytes and 123MB until OOM
Something like MemoryMapFile may do the trick but it's for files didn't get anything for images in FileChannel
UPDATE
I can't scale down images while displaying cause I have to edit them (i.e. user can write something over the images, or place other images as watermark over them)
Thanks
As you say:
I can't go for bitmap sampling as I can't change the size of images.
so, reduce image size or sample it before show, is not ok. I think below my be a solution.
Because Android's dalvik limit, there is a max heap size you can use. When exceed it, crash happen.
In order to bypass dalvik limit, you can use jni to apply for memory from native heap. You must use libjpeg in jni. You can choose show image on SurfaceView, then by holder.getSurface() to get a Surface Object, pass it to jni, then use libjpeg lib to decode image and show it. Next is some example code, call showJPG() function to display large image.
JNIEXPORT void JNICALL Java_com_example_photoprocessing_activity_SurfaceProcessingActivity_showJPG(
JNIEnv * env, jobject activity, jobject surface, jstring img) {
const char * imgChar;
jboolean * isCopy;
imgChar = env->GetStringUTFChars(img, 0);
ANativeWindow_Buffer nwBuffer;
LOGI("img path : %s ",imgChar);
LOGI("ANativeWindow_fromSurface ");
ANativeWindow * mANativeWindow = ANativeWindow_fromSurface(env, surface);
if (mANativeWindow == NULL) {
LOGE("ANativeWindow_fromSurface error");
return;
}
LOGI("ANativeWindow_lock ");
if (0 != ANativeWindow_lock(mANativeWindow, &nwBuffer, 0)) {
LOGE("ANativeWindow_lock error");
return;
}
read_jpeg_file_show(imgChar, nwBuffer);
if (nwBuffer.format == WINDOW_FORMAT_RGBA_8888) {
LOGI("nwBuffer->format == WINDOW_FORMAT_RGBA_8888 ");
}
LOGI("ANativeWindow_unlockAndPost ");
if (0 != ANativeWindow_unlockAndPost(mANativeWindow)) {
LOGE("ANativeWindow_unlockAndPost error");
return;
}
env->ReleaseStringUTFChars(img,imgChar);
ANativeWindow_release(mANativeWindow);
LOGI("ANativeWindow_release ");
return;
}
int read_jpeg_file_show(const char *input_filename,
ANativeWindow_Buffer& nwBuffer) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *input_file;
JSAMPARRAY buffer;
int row_width;
unsigned char *buffertmp;
cinfo.err = jpeg_std_error(&jerr);
if ((input_file = fopen(input_filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", input_filename);
LOGI("can't open jpg1");
return -1;
}
jpeg_create_decompress(&cinfo);
/* Specify data source for decompression */
jpeg_stdio_src(&cinfo, input_file);
/* Read file header, set default decompression parameters */
(void) jpeg_read_header(&cinfo, TRUE);
/* Start decompressor */
(void) jpeg_start_decompress(&cinfo);
row_width = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE,
row_width, 1);
buffertmp = (unsigned char *) malloc(row_width);
memset(buffertmp, 0, row_width);
LOGI("malloc and memset");
/* Process data */
int get8h5 = 248, get8h6 = 252;
__uint16_t * line = (__uint16_t *) nwBuffer.bits;
int wheight = 0;
int scalew = 1, scaleh = 1;
if (cinfo.output_width > nwBuffer.width) {
scalew = cinfo.output_width / nwBuffer.width;
}
LOGI(" scale of img = %d", scalew);
for (int i = 0, choosehNum = 0; i < cinfo.output_height; i++) {
jpeg_read_scanlines(&cinfo, buffer, 1);
buffertmp = *buffer;
if (i % scalew == 0 && choosehNum++ < nwBuffer.height) {
//LOGI("nwBuffer->format == WINDOW_FORMAT_RGB_565");
for (int j = 0, choosewNum = 0; j < cinfo.output_width; j++) {
if (j % scalew == 0) {
if (nwBuffer.format == WINDOW_FORMAT_RGB_565) {
line[choosewNum] = ((__uint16_t ) buffertmp[3 * j + 0]
& get8h5) << 8
| ((__uint16_t ) (buffertmp[3 * j + 1] & get8h6)
<< 3)
| ((__uint16_t ) (buffertmp[3 * j + 2] & get8h6)
>> 3);
choosewNum++;
}
}
}
line = line + nwBuffer.stride;
}
}
(void) jpeg_finish_decompress(&cinfo);
LOGI("jpeg_finish_decompress !!");
jpeg_destroy_decompress(&cinfo);
LOGI("jpeg_destroy_decompress !!");
/* Close files, if we opened them */
fclose(input_file);
return 0;
}
Hope to help you!
It's clear that you must reduce the image size or your phone can't render the image to show in your ImageView. So, You need to scale down the image so that image can be shown in your ImageView.
int imageScaleWidth = 480; // Change the width
Bitmap bitmapImage = BitmapFactory.decodeFile(imgDecodableString); // imgDecodableString is your image path.
int imageScaleHeigth = (int) ( bitmapImage.getHeight() * (imageScaleWidth / bitmapImage.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(bitmapImage, imageScaleWidth , imageScaleHeigth , true);
imgView.setImageBitmap(scaled);
You should read the Android docs on "Loading Large Bitmaps Efficiently"
In particular, you mention you can't for for "bitmap sampling" but notice that Android allows you to read the image file size and then calculate a sampling that will work for the size available. Android will then load only the sampled bitmap, which is fine because the screen resolution isn't sufficient to properly display images like this anyway.
Also, be sure to consider the method decodeSampledBitmapFromResource for thumbnails.
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap
And last, be sure to use the largeHeap Manifest attribute, if needed - and look at other memory optimization techniques based on the docs:
https://developer.android.com/training/articles/memory.html

Reading image data from a JFIF?

So I'm reading JFIF (JPEG) data from a file, as an exercise (I know there are libraries out there that already do this, I'm not looking for those). I've already got the image file size, color depth, and dimensions. However, I'm not too sure how to get the actual image data. I've looked at the data in a hex editor, and comparing that against the actual image leads me nowhere. If anyone has a good resource to start on this (I know it's probably an arduous and enlightening process, but that's why I'm doing it), that would be awesome.
My code so far, just for context:
// check header data, assign header data to important fields
// Start Of Image (SOI) must be FFD8 and the next marker must be FF
if(!(this.data[0] == (byte) 0xFF && this.data[1] == (byte) 0xD8
&& this.data[2] == (byte) 0xFF))
this.isValid = false;
// check if file is not valid
if(!isValid)
log.log(Level.SEVERE,
String.format("ERROR: File %s is not registered as a JFIF!\n", this.filename),
new IllegalArgumentException());
// If the next values are correct, then the data stream starts at SOI
// If not, the data stream is raw
this.isRawDataStream = !(this.data[3] == (byte) 0xE0
&& this.data[6] == (byte) 0x4A
&& this.data[7] == (byte) 0x46
&& this.data[8] == (byte) 0x49
&& this.data[9] == (byte) 0x46
&& this.data[10] == (byte) 0x00);
// Read until SOF0 marker (0xC0)
int i = 11;
while(this.data[i] != (byte) 0xC0) {
i++;
}
System.out.println("SOF0 marker at offset " + i);
// Skip two bytes, next byte is the color depth
this.colorDepth = this.data[i+3];
// Next two bytes are the image height
String h = String.format("%02X", this.data[i+4]) + String.format("%02X", this.data[i+5]);
this.height = hexStringToInt(h);
System.out.println("Height: " + this.height);
// Next two bytes are the image width
String w = String.format("%02X", this.data[i+6]) + String.format("%02X", this.data[i+7]);
this.width = hexStringToInt(w);
System.out.println("Width: " + this.width);
System.out.println("Color depth: " + this.colorDepth);
// load pixels into an image
this.image = new BufferedImage(this.width,
this.height,
BufferedImage.TYPE_INT_RGB);
Then, I need to get each pixel and send it to the image. How would I get each pixel and its respective RGB data?
What you are trying to do is not a simple afternoon project. This book explains the process: There is A LOT of code between JPEG compressed data and pixel values.
http://www.amazon.com/Compressed-Image-File-Formats-JPEG/dp/0201604434/ref=pd_bxgy_b_img_y
First of all, you have to deal with two separate but related compression methods: Sequential and progressive.
As you read the bit data, you have to
Huffman decode
Run length decode
Inverse Quantization
List item
Inverse Discrete Cosine Transform
Up sample
YCbCr to RGB convert
That's in the simple case of sequential.
You are not going to get all of those steps explained on this forum.
I also recommend
http://www.amazon.com/dp/1558514341/ref=rdr_ext_tmb

Is there a equivalent of Android's BitmapFactory.Options isDecodeBounds for TIFF in Java/JAI?

I am trying to improve the performance of our system (a Java app running in Tomcat) and now the bottleneck is in one operation, we need to read and return dimension of tiff images, so we use JAI's ImageDecoder and use
ImageDecoder decoder = ImageCodec.createImageDecoder("TIFF", input, param);
RenderedImage r = decoder.decodeAsRenderedImage();
int width = r.getWidth();
int height = r.getHeight();
From sampling data, a lot of time is spent in createImageDecoder. My assumption (without going to source code of ImageCodec) is it's probably trying to decode the input stream.
Coming from Android land, I am hoping there is a similar solution to just decode bounds like setting BitmapFactory.Options.inJustDecodeBounds = true but so far no luck in finding any other library like that. (I am aware that tiff support on Android is missing in AOSP, but that's topic for another day.)
Anyone know a library that does this? Or is there a way to achieve similar goal using JAI/ImageIO?
It looks like the tiff file format groups this information together in a header, so you could just read the data from the file yourself:
private static Dimension getTiffDimensions(InputStream tiffFile) throws IOException {
ReadableByteChannel channel = Channels.newChannel(tiffFile);
ByteBuffer buffer = ByteBuffer.allocate(12);
forceRead(channel, buffer, 8);
byte endian = buffer.get();
if(endian != buffer.get() || (endian != 'I' && endian != 'M')) {
throw new IOException("Not a tiff file.");
}
buffer.order(endian == 'I' ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
if(buffer.getShort() != 42) {
throw new IOException("Not a tiff file.");
}
// Jump to the first image directory. Note that we've already read 8 bytes.
tiffFile.skip(buffer.getInt() - 8);
int width = -1;
int height = -1;
// The first two bytes of the IFD are the number of fields.
forceRead(channel, buffer, 2);
for(int fieldCount = buffer.getShort(); fieldCount > 0 && (width < 0 || height < 0); --fieldCount) {
forceRead(channel, buffer, 12);
switch(buffer.getShort()) {
case 0x0100: // Image width
width = readField(buffer);
break;
case 0x0101: // Image "length", i.e. height
height = readField(buffer);
break;
}
}
return new Dimension(width, height);
}
private static void forceRead(ReadableByteChannel channel, ByteBuffer buffer, int n) throws IOException {
buffer.position(0);
buffer.limit(n);
while(buffer.hasRemaining()) {
channel.read(buffer);
}
buffer.flip();
}
private static int readField(ByteBuffer buffer) {
int type = buffer.getShort();
int count = buffer.getInt();
if(count != 1) {
throw new RuntimeException("Expected a count of 1 for the given field.");
}
switch(type) {
case 3: // word
return buffer.getShort();
case 4: // int
return buffer.getInt();
default: // char (not used here)
return buffer.get() & 0xFF;
}
}
I've tested this with a few different tiff files (run length encoded black & white, color with transparency) and it seems to work fine. Depending on the layout of your tiff file it may have to read a lot of the stream before it finds the size (one of the files I tested, saved by Apple's Preview, had this data at the end of the file).

How can I print an image on a Bluetooth printer in Android?

I have to print some data on thermal bluetooth printer, I'm doing with this:
String message="abcdef any message 12345";
byte[] send;
send = message.getBytes();
mService.write(send);
It works well for text, but not for images. I think I need to get the byte[] of the image data. I tried getting the data of the image this way:
Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.qrcode);
ByteArrayOutputStream stream=new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
byte[] image=stream.toByteArray();
Unfortunately the printer prints a lot of strange characters (approx. 50 cm of paper). I don't know how to print the image.
I would like to try getting the pixels of the bitmap and next converting it to a byte[] and sending it, but i don't know how to do it.
Thanks
UPDATE:
After so much time, i'm doing this: I have a method called print_image(String file), the which gets the path of the image that i want to print:
private void print_image(String file) {
File fl = new File(file);
if (fl.exists()) {
Bitmap bmp = BitmapFactory.decodeFile(file);
convertBitmap(bmp);
mService.write(PrinterCommands.SET_LINE_SPACING_24);
int offset = 0;
while (offset < bmp.getHeight()) {
mService.write(PrinterCommands.SELECT_BIT_IMAGE_MODE);
for (int x = 0; x < bmp.getWidth(); ++x) {
for (int k = 0; k < 3; ++k) {
byte slice = 0;
for (int b = 0; b < 8; ++b) {
int y = (((offset / 8) + k) * 8) + b;
int i = (y * bmp.getWidth()) + x;
boolean v = false;
if (i < dots.length()) {
v = dots.get(i);
}
slice |= (byte) ((v ? 1 : 0) << (7 - b));
}
mService.write(slice);
}
}
offset += 24;
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
}
mService.write(PrinterCommands.SET_LINE_SPACING_30);
} else {
Toast.makeText(this, "file doesn't exists", Toast.LENGTH_SHORT)
.show();
}
}
I did it based on this post
This is the class PrinterCommands:
public class PrinterCommands {
public static final byte[] INIT = {27, 64};
public static byte[] FEED_LINE = {10};
public static byte[] SELECT_FONT_A = {27, 33, 0};
public static byte[] SET_BAR_CODE_HEIGHT = {29, 104, 100};
public static byte[] PRINT_BAR_CODE_1 = {29, 107, 2};
public static byte[] SEND_NULL_BYTE = {0x00};
public static byte[] SELECT_PRINT_SHEET = {0x1B, 0x63, 0x30, 0x02};
public static byte[] FEED_PAPER_AND_CUT = {0x1D, 0x56, 66, 0x00};
public static byte[] SELECT_CYRILLIC_CHARACTER_CODE_TABLE = {0x1B, 0x74, 0x11};
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
public static byte[] SET_LINE_SPACING_24 = {0x1B, 0x33, 24};
public static byte[] SET_LINE_SPACING_30 = {0x1B, 0x33, 30};
public static byte[] TRANSMIT_DLE_PRINTER_STATUS = {0x10, 0x04, 0x01};
public static byte[] TRANSMIT_DLE_OFFLINE_PRINTER_STATUS = {0x10, 0x04, 0x02};
public static byte[] TRANSMIT_DLE_ERROR_STATUS = {0x10, 0x04, 0x03};
public static byte[] TRANSMIT_DLE_ROLL_PAPER_SENSOR_STATUS = {0x10, 0x04, 0x04};
}
As is seen in the print_image method I'm calling a method, called convertBitmap, and im sending a bitmap, this is the code:
public String convertBitmap(Bitmap inputBitmap) {
mWidth = inputBitmap.getWidth();
mHeight = inputBitmap.getHeight();
convertArgbToGrayscale(inputBitmap, mWidth, mHeight);
mStatus = "ok";
return mStatus;
}
private void convertArgbToGrayscale(Bitmap bmpOriginal, int width,
int height) {
int pixel;
int k = 0;
int B = 0, G = 0, R = 0;
dots = new BitSet();
try {
for (int x = 0; x < height; x++) {
for (int y = 0; y < width; y++) {
// get one pixel color
pixel = bmpOriginal.getPixel(y, x);
// retrieve color of all channels
R = Color.red(pixel);
G = Color.green(pixel);
B = Color.blue(pixel);
// take conversion up to one single value by calculating
// pixel intensity.
R = G = B = (int) (0.299 * R + 0.587 * G + 0.114 * B);
// set bit into bitset, by calculating the pixel's luma
if (R < 55) {
dots.set(k);//this is the bitset that i'm printing
}
k++;
}
}
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, e.toString());
}
}
This is the printer that i'm using, resolution: 8 dots/mm, 576 dots/line
And this is what I like to do (i did it with the same printer, but with an app downloaded from play store)
This is what i'm getting now
Closer:
Closer2:
A little part of the image can be seen, so I think that i'm closer to can print the image...
The image that i'm using is this (576x95):
And this is the converted image (i'm converting it with the upper code):
So, the answer is: what I'm doing wrong?, I think that the error is in this command:
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
But, how can I calculate the correct values for my image?, thanks
I solve it converting Bitmap to Byte array. Remember that your image must be black & white format.
For full source code:
https://github.com/imrankst1221/Thermal-Printer-in-Android
public void printPhoto() {
try {
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.img);
if(bmp!=null){
byte[] command = Utils.decodeBitmap(bmp);
printText(command);
}else{
Log.e("Print Photo error", "the file isn't exists");
}
} catch (Exception e) {
e.printStackTrace();
Log.e("PrintTools", "the file isn't exists");
}
}
I also tried this and I got to my own solution and I think I figured out how the SELECT_BIT_IMAGE_MODE command works.
The command public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3}; in the class PrinterCommands is the POS Command for image printing.
The first two are pretty standard, the next three determine the mode and the dimensions of the image to be printed. For the sake of this solution, let's just assume that the second element (33, we are indexed zero) is always 33.
The last two elements of that byte[] refers to the Width (in pixels) property of the image that you want to print, element 3 is sometimes referred to as nL and element 4 is sometimes referred to as nH. They are actually both referring to the Width, nL is the Low Byte while nH is the High Byte. This means that we can have at the most an image with a width of 1111 1111 1111 1111b (binary) which is 65535d (decimal), though I haven't tried it yet. If nL or nH aren't set to the proper values, then there will be trash characters printed along with the image.
Somehow, Android docs tells us that the limits of the value for a byte in a byte array is -128 and +127, when I tried to put in 255, Eclipse asked me to cast it to Byte.
Anyway, going back to nL and nW, for your case, you have an image with width 576, if we convert 576 to Binary, we get two bytes which would go like:
0000 0010 0100 0000
In this case, the Low Byte is 0100 0000 while the High Byte is 0000 0010. Convert it back to decimal and we get nL = 64 and nH = 2.
In my case, I printed an image that has width of 330px, converting 330 to binary we get:
0000 0001 0100 1010
In this case now, the Low Byte is 0100 1010 and the High Byte is 0000 0001. Converting to decimal, we get nL = 74 and nH = 1.
For more information, look at these documentation/tutorials:
Star Asia Mobile Printer Documentation
ECS-POS programming guide - really extensive
Another Documentation
The expanded version of the code above, with more explanation
Explanation of the code above
Hope these helps.
Solved!, I was doing a wrong printer initializing... The corect way is:
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3};
So, by this way the image is printed completely fine
EDIT: Update based on reading your question: https://stackoverflow.com/questions/16597789/print-bitmap-on-esc-pos-printer-java
I will assume that the printer you are printing to is the same as above, i.e. the Rego Thermal Printer. This, as you note, support the ESC/POS Page Description Language.
Printers interpret data streamed to them as either a marked up document (in a similar way to how browser interpret HTML). In some cases, the printer literally runs the document as a program (eg PostScript). Link: Page Description Languages.
Common languages are:
Yours: ESC/POS.
PostScript
PCL
ZPL
You need to read the specifications for your printer to determine which language to use - if you need to support any printer, then you have a very large job ahead of you :(
In ESC/POS, you will need to use the GS v 0 command (documented on p33). You do this by sending the the characters 0x1D7630 across the serial link, followed by a set of arguments:
ASCII: Gs v 0
Decimal: 29 118 48 m xL xH yL yH [d]k
Hexadecimal: 1D 76 30 m xL xH yL yH [d]k
Parameter definitions:
m:
0,48: normal mode (1:1 scale)
1,49: double-width
2,50: double-height
3,51: double-width + double-height
xL, xH specifies (xL + xH × 256) bytes in horizontal direction for the bit image.
yL, yH specifies (yL + yH × 256) dots in vertical direction for the bit image.
[d]k specifies the bit image data (raster format).
k indicates the number of bit image data. k is an explanation parameter; therefore, it does not need to be transmitted.
Notes:
When data [d]k is 1 specifies a bit printed to 1 and not printed to 0.
If a raster bit image exceeds one line of print area, the excess data is not printed.
This command executes paper feed for amount needed for printing the bit image regardless of the settings by ESC 2 or ESC 3.
After printing the bit image, this command sets the print position to the beginning of the line, and clears up the buffer.
When this command is executed, the data is transmitted and printed synchronously. So no other printing command is required.
There are several more extensive expositions:
http://nicholas.piasecki.name/blog/2009/12/sending-a-bit-image-to-an-epson-tm-t88iii-receipt-printer-using-c-and-escpos/
On SO in C#. While not Java, it is close enough to be a template.
Unfortunately there is no printer API in Android. If you feel strongly about this, do follow these issues:
https://code.google.com/p/android/issues/detail?id=40486
https://code.google.com/p/android/issues/detail?id=1148
I am new to ESC/POS and am struggling with it. I came across this page which seems to have some useful functions: http://code.taobao.org/p/printer/src/trunk/prtest/src/com/enjar/plugins/PrintTools_58mm.java
It's in Chinese though, but might be worth going through. If anybody figures it out, I would like to get enlightened too...
I know for evolute and AMDL bluetooth printers.First Read the protocol defination document of the printer that tells you what specific bytes you need for the device-
public void connect() throws Exception
{
BluetoothDevice printer = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(connParams);
Method m = printer.getClass().getMethod("createInsecureRfcommSocket",new Class[] { int.class });
sock = (BluetoothSocket)m.invoke(printer, Integer.valueOf(1));
sock.connect();
os=sock.getOutputStream();
in=sock.getInputStream();
}
After connecting through the above code you get the outputstream of the socket.Then convert your image to the corresponding byte through the tool provided with the printer you get something like
public byte[] Packet1={
(byte)0X8A,(byte)0XC6,(byte)0X94,(byte)0XF4,(byte)0X0B,(byte)0X5E,(byte)0X30,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X04,(byte)0X24,(byte)0X01,(byte)0X0C,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X01,(byte)0X08,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X04,(byte)0X24,(byte)0X05,(byte)0X0C,(byte)0X00,(byte)0X60,(byte)0X00,(byte)0X18,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X30,(byte)0X1E,(byte)0X10,(byte)0X60,(byte)0X00,(byte)0X18,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X70,(byte)0X3F,(byte)0X18,(byte)0XF0,(byte)0X00,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X70,(byte)0X3C,(byte)0X39,(byte)0XF1,(byte)0X80,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF8,(byte)0X7C,(byte)0X9F,(byte)0XF1,(byte)0X80,(byte)0X7F,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF9,(byte)0X9E,(byte)0X1C,(byte)0XFF,(byte)0XC2,(byte)0X7E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF9,(byte)0X9E,(byte)0X1C,(byte)0XE7,(byte)0XE2,(byte)0X7E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XFB,(byte)0X1E,(byte)0X1C,(byte)0XFF,(byte)0XE7,(byte)0XBE,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X7B,(byte)0X16,(byte)0X1C,(byte)0XFF,(byte)0XDF,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X71,(byte)0X12,(byte)0X1C,(byte)0XE7,(byte)0XF7,(byte)0X34,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X51,(byte)0X12,(byte)0X1C,(byte)0XF7,(byte)0XF7,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X12,(byte)0X1C,(byte)0XFF,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X12,(byte)0X3F,(byte)0XFD,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X96,(byte)0X3F,(byte)0XFC,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X05,(byte)0X49,(byte)0X80,(byte)0X00,(byte)0X08,(byte)0X10,(byte)0X5E,(byte)0X28,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X30,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XE0,(byte)0X74,(byte)0XA9,(byte)0X33,(byte)0X23,(byte)0X26,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)0X04
};
where 8A is starting byte C6 is mode byte (different for smart card , swipe and fingerprint) , 94 is font byte and last byte 04 is the end byte telling the hardware that this is end of the packet.Depending on size of the image you get several of these packets of length 256 byte (most printer).Write them to the outputStream.
os.write(Packet1)
This works for me:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inTargetDensity = 200;
options.inDensity = 200;
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), img, options);
Before using the BitmapFactory.Options, I was able to print only 60X60 image size, now i can print images of greater size as well.
use this code:
public static void print(Context context) {
String examplePath = "file:///sdcard/dcim/Camera/20111210_181524.jpg";
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("image/jpeg");
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Photo");
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(examplePath));
sendIntent.putExtra(Intent.EXTRA_TEXT, "Enjoy the photo");
context.startActivity(Intent.createChooser(sendIntent, "Email:"));
}

Video encode from sequence of images from java android [duplicate]

This question already has answers here:
Encode a series of Images into a Video
(4 answers)
Closed 8 years ago.
I would like to encode video from sequence of images with java only in my current android project. I mean without any use of external tools such as NDK.
Also is there any availability of java libraries for encoding video from sequence of images ?
You can use a pure java library called JCodec ( http://jcodec.org ). It contains a primitive yet working H.264 ( AVC ) encoder and a fully functioning MP4 ( ISO BMF ) muxer.
Here's a CORRECTED code sample that uses low-level API:
public void imageToMP4(BufferedImage bi) {
// A transform to convert RGB to YUV colorspace
RgbToYuv420 transform = new RgbToYuv420(0, 0);
// A JCodec native picture that would hold source image in YUV colorspace
Picture toEncode = Picture.create(bi.getWidth(), bi.getHeight(), ColorSpace.YUV420);
// Perform conversion
transform.transform(AWTUtil.fromBufferedImage(bi), yuv);
// Create MP4 muxer
MP4Muxer muxer = new MP4Muxer(sink, Brand.MP4);
// Add a video track
CompressedTrack outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25);
// Create H.264 encoder
H264Encoder encoder = new H264Encoder(rc);
// Allocate a buffer that would hold an encoded frame
ByteBuffer _out = ByteBuffer.allocate(ine.getWidth() * ine.getHeight() * 6);
// Allocate storage for SPS/PPS, they need to be stored separately in a special place of MP4 file
List<ByteBuffer> spsList = new ArrayList<ByteBuffer>();
List<ByteBuffer> ppsList = new ArrayList<ByteBuffer>();
// Encode image into H.264 frame, the result is stored in '_out' buffer
ByteBuffer result = encoder.encodeFrame(_out, toEncode);
// Based on the frame above form correct MP4 packet
H264Utils.encodeMOVPacket(result, spsList, ppsList);
// Add packet to video track
outTrack.addFrame(new MP4Packet(result, 0, 25, 1, 0, true, null, 0, 0));
// Push saved SPS/PPS to a special storage in MP4
outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList));
// Write MP4 header and finalize recording
muxer.writeHeader();
}
You can download JCodec library from a project web site or via Maven, for this add the below snippet to your pom.xml:
<dependency>
<groupId>org.jcodec</groupId>
<artifactId>jcodec</artifactId>
<version>0.1.3</version>
</dependency>
[UPDATE 1] Android users can use something like below to convert Android Bitmap object to JCodec native format:
public static Picture fromBitmap(Bitmap src) {
Picture dst = Picture.create((int)src.getWidth(), (int)src.getHeight(), RGB);
fromBitmap(src, dst);
return dst;
}
public static void fromBitmap(Bitmap src, Picture dst) {
int[] dstData = dst.getPlaneData(0);
int[] packed = new int[src.getWidth() * src.getHeight()];
src.getPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());
for (int i = 0, srcOff = 0, dstOff = 0; i < src.getHeight(); i++) {
for (int j = 0; j < src.getWidth(); j++, srcOff++, dstOff += 3) {
int rgb = packed[srcOff];
dstData[dstOff] = (rgb >> 16) & 0xff;
dstData[dstOff + 1] = (rgb >> 8) & 0xff;
dstData[dstOff + 2] = rgb & 0xff;
}
}
}

Categories