Electronjs ile barkod yazdırma

Electronjs ile barkod yazdırma

Merhabalar :)

Bu yazı sizleri min. düzeyde nodejs, javascript, temel html, css konularında bilgili varsayar

Senaryo:

Bir barkod yazıcınız olduğunu ve bir arayüz ile daha önce tasarlanmış bir barkodun, bu arayüz ile ilgili değişkenler girilerek yazıcıya yazdırılma durumunu inceleyeceğiz.

Arayüz için electronjs, barkod tasarımı için ZebraDesginer programı, yazıcı olarak da Zebra GC420T kullanacağız.

Şimdi senaryoyu okuduğunuz halde buraya kadar geldiyseniz muhtemelen barkod tasarımı konusunda en azından bir ön bilginiz olduğunu kabul ederek ve konuyu çok uzatmamak adına kısa bir şekilde barkod tasarımına değinelim.

  1. ZebraDesginer programını indirelim ama öncelikle şunu bilmeniz gerekiyor. Bilgisayarınızda zebra yada programın desteklediği bir barkod yazıcısı kurulu olmalı bunu sağladığınızı varsayarak programın ekranını aşağıya bırakıyorum.
  • Yazıcı Seçimi

image.png

  • Özellikler

image.png

Seçimleri yaparken yazıcı ve etiket tasarımınıza dikkat edin, çünkü yaptığımız her bir seçim sonrasında elde edeceğimiz .prn dosyasına işlenecek

  • Özellikler 2

image.png

  • Örnek barkod tasarımı :)

image.png

Tasarımınızı bu şekilde yaptıktan sonra yapmanız gereken yazdır dedikten sonra etiketi dosyaya yazdır seçeneğini seçmektir. Daha sonra size dosyayı nereye kaydedeceğini soran pencere açılacaktır. Böylece biz aslında yazıcıya gidecek olan içeriği elde etmiş olacağız.

image.png

Kaydettiğiniz dosyayı not defteri ile açtığınızda aşağıda görülen şekilde içeriği elde etmiş olacağız burada önemli olan aslında tasarım yaparken belirttiğimiz şirket adı, ürün adı ve barkod numarası. Barkod yazıcılar çeşitli programlama dillerini (PPLA) algılayıp buna göre çıktı verecek şekilde üretilirler. Bu durumu yazıcı alırken göz önünde bulundurunuz.

image.png

işaretli alanlara dikkat ediniz daha sonra buraları kendimize göre değiştireceğiz. Değiştirmek için farklı şekilde de bırakabilirsiniz. Neticede sistem bu içeriği hafızaya alacak, değişken içeriklerini metinde arayıp değiştirdikten sonra yazıcıya gönderecektir.

  1. ElectronJs ile arayüz kodlamasına geçelim.

Şimdi fark ettiyseniz ayrıntılara girdikçe yazı can sıkıcı bir şekilde uzamaya başlıyor yüzden genel hatlara değineceğim. Yazının sonunda paylaşacağım github reposu ile kodlara ulaşmanız mümkün olacak,

Electron temelde html, css ve JavaScript kullanarak yani bir web sitesi gibi tasarım yaparak, bu tasarımı nodejs yetenekleri ile birleştirerek platform bağımsız uygulama geliştirmemizi sağlar

  • Electron'u bilgisayara kurma

NodeJs'in bilgisayarınızda kurulu olduğunu varsayarak aşağıdaki komut ile olduğunuz dizinde geçerli sürümü kuruyoruz. Bende package.json dosyasına göre "electron": "^7.1.5", sürümü, ve pakatleme için "electron-packager": "^14.1.1" sürümünü ve electronun kurulu plartforma uygun şekilde build edilmesini sağlayan "electron-rebuild": "^1.8.8" paketlerini kurar.

npm i -D electron@latest

Electronu dizine kurduysanız node_modules klasöründe gerekli nodejs modülleri, main.js ile electron komutlarını yürüteceğimiz js dosyası ve index.html ile arayüz tasarımı yapacağımız dosyanın aynı zamanda kurulum ve diğer ayarlamarın yapıldığı package.json dosyalarının oluştuğunu göreceksiniz.

  • package.json dosyası
{
  "name": "barcodeprint",
  "version": "1.0.0",
  "description": "Barcode Print For Electron",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "package-win": "electron-packager . barcodeprint --overwrite  --platform=win32 --arch=x64 --icon=./assets/img/print.ico --prune=true --out=../barcodeprint"
  },
  "keywords": [
    "barcode",
    "electron"
  ],
  "author": "Savaş HASÇELİK",
  "license": "MIT",
  "devDependencies": {
    "electron": "^7.1.5",
    "electron-packager": "^14.1.1",
    "electron-rebuild": "^1.8.8"
  },
  "dependencies": {
    "app": "^0.1.0",
    "iconv": "^2.3.5",
    "jquery": "^3.4.1"
  }
}

Burada bahsedilmesi gereken önemli bir husus da electronun paketleme işlemini yürütecek script kodu olan "package-win" komutur. Siz isterseniz buna "...." farkli bir isim de verebilirsiniz.

Electron ilgili dizine indikten sonra package.json dosyasını yukarıdaki gibi düzenlemek yeterli olacaktır.

Bundan sonra şu komutu girmeniz gerekecektir.

npm install

Bu komutu girdikten sonra yapmamız gereken index.html ile arayüzü tasarlamak olacak.

Index.html'de arayüz tasarım ve işlemleri içiin bootstrap kullanacağız.

<!DOCTYPE html>
<html>
  <head>
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <meta http-equiv="content-security-policy"
    content="default-src 'none'; script-src 'self';
    connect-src 'self'; img-src 'self';
    style-src 'self' 'sha256-CwE3Bg0VYQOIdNAkbB/Btdkhul49qZuwgNCMPgNY5zw=';" />
    <title>Barcode Print</title>
    <link rel="stylesheet" href="./assets/css/bootstrap.min.css"  crossorigin="anonymous">


  </head>

  <body >

<div class="container row">

  <div class="col-md-4">

    <img id="copy" src="./assets/img/paste1.png" height="160px" alt="">
    <!-- /varsayılan yazıcının adının geldiği alan -->

    <input id="printers" type="text" class="form-control" value="" disabled>
  </div>

  <div class="col-md-8">

    <div class="input-group mb-2">
      <!-- /şirket isminin alanı -->

      <input id="sirket" type="text" class="form-control" placeholder="Şirket Adı">
    </div>
    <div class="input-group  mb-2">
      <!-- /ürün adının geldiği alan -->

      <input id="urunadi" type="text" class="form-control" placeholder="Ürün Adı">
    </div>


    <div class="input-group  mb-2">
      <!-- /ürün barkodunun geldiği alan -->
      <input id="barcode" type="text" class="form-control" placeholder="Barcode">
    </div>

    <div class="input-group  mb-2">


      <button id="print" class="btn btn-success btn-block" disabled >Print

        <span class="badge badge-light">
          <!-- /yazıcıya gönderilecek barkod adedi -->
        <a href="#" id="count" >1</a>

        </span>

      </button>

    </div>
    <!-- /kopyalanan verinin bilgilendirme alanı -->
    <div class="col-md-8 ">
      <p >CTRL + V   ||  Yenile: CTRL + R</p>
    </div>

  </div>

</div>

    <script type="text/javascript" src="./assets/js/jquery.min.js"></script> 

    <script type="text/javascript" src="./assets/js/renderer.js"></script> 

  </body>
</html>

Index.html içinde script olarak çağırdığımız renderer.js arayüz işlemleri ve barkod'un yazıcıya gönderilmesinden sorumlu olacak.

Bundan sonra en önemli kısım aslında main.js ve renderer.js dosyalarıdır. Bu dosyalardan main.js electronjs'in ana işlemlerinden sorumludur.

Öncelikle main.js'e göz atacak olursak

// Modules to control application life and create native browser window
const {app, BrowserWindow,electron} = require('electron')

const path = require('path')
const ipc = require('electron').ipcMain


// this should be placed at top of main.js to handle setup events quickly
if (handleSquirrelEvent(app)) {
    // squirrel event handled and app will exit in 1000ms, so don't do anything else
    return;
}

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 250,
    resizable: false,
    webPreferences: {
      nodeIntegration: true,
      preload: path.join(__dirname, './assets/js/preload.js'),


    }
  })
  mainWindow.setMenuBarVisibility(false)
  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') app.quit()
})

app.on('activate', function () {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) createWindow()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
function handleSquirrelEvent(application) {
  if (process.argv.length === 1) {
      return false;
  }

  const ChildProcess = require('child_process');


  const appFolder = path.resolve(process.execPath, '..');
  const rootAtomFolder = path.resolve(appFolder, '..');
  const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));
  const exeName = path.basename(process.execPath);

  const spawn = function(command, args) {
      let spawnedProcess, error;

      try {
          spawnedProcess = ChildProcess.spawn(command, args, {
              detached: true
          });
      } catch (error) {}

      return spawnedProcess;
  };

  const spawnUpdate = function(args) {
      return spawn(updateDotExe, args);
  };

  const squirrelEvent = process.argv[1];
  switch (squirrelEvent) {
      case '--squirrel-install':
      case '--squirrel-updated':
          // Optionally do things such as:
          // - Add your .exe to the PATH
          // - Write to the registry for things like file associations and
          //   explorer context menus

          // Install desktop and start menu shortcuts
          spawnUpdate(['--createShortcut', exeName]);

          setTimeout(application.quit, 1000);
          return true;

      case '--squirrel-uninstall':
          // Undo anything you did in the --squirrel-install and
          // --squirrel-updated handlers

          // Remove desktop and start menu shortcuts
          spawnUpdate(['--removeShortcut', exeName]);

          setTimeout(application.quit, 1000);
          return true;

      case '--squirrel-obsolete':
          // This is called on the outgoing version of your app before
          // we update to the new version - it's the opposite of
          // --squirrel-updated

          application.quit();
          return true;
  }
};

main.js içinde createWindow() fonksiyonu ile yaratacağımız pencere özelliklerini belirlemiş oluyoruz.

Burada değinmesek de squirrelEvent sabiti ile kurulum yada update işlemlerinde kısayol gibi tanımlamaları yapabiliriz.

Şimdi de asıl işlemleri yürüttüğümüz dosyamız olan renderer.js dosyasına bakalım.

Bu dosyada;

Kullanım kolaylığı olsun diye, sonuçta her datayı elle yazacak halimiz yok diye düşünerekten :) excel programından aşağıdaki resimde görüldüğü gibi datayı kopyalalayıp yapıştırdığımızda alanların dolmasını sağlayacağız

image.png

Ardından ZebraDesigner programında dosyaya yazdırdığımız barcode.prn dosyasını çağırıp index.html içinde inputlara girilen değerleri eski değerlerle değiştirip, kaydettikten sonra oluşan dosyayı print butonuna tıklandığında varsayılan yazıcıya göndermek işlemlerini yapacağız.

Bu işlemlerin gerçekleştirildiği renderer.js'in içeriğine bakalım

const { remote } = window.require ('electron');
const path = window.require ('path');
const fs = window.require('fs');
var child = window.require('child_process').exec;
var Iconv  = window.require('iconv').Iconv;

execPath = path.dirname (remote.process.execPath);
window.$ = window.jQuery = require('./assets/js/jquery.min.js')
var prnFileContent="";
var prnfile =execPath+"\\resources\\app\\assets\\barcode\\barcode.prn";


//barkod adedi
$("#count").on("click",function(e){
  count=$(this).html();
  count++;
  $(this).html(count);
})

//Yazıcı listesini al ve ilk sıradaki (varsayılan) yazıcıyı değişkene atar
child('wmic printer where "default=True" get name/value',(error, stdout, stderr) => {
    if (error) {
        alert("Yazıcılara erişim hatası",error);
      }
      var yaziciadi =stdout.split("=");
      $("#printers").val(yaziciadi[1]);
  });

//kopyalanan dosya içeriği burada değiştirilir ve arayüzde elementlerin bazı özellikleri değiştirilir
  function readTextFile(prnfile)
  {
    var data= fs.readFileSync(prnfile,{encoding:"latin1"});         
                   var iconv = new Iconv('UTF-8', 'ascii//TRANSLIT');
                   prnFileContent=data;
                   console.log("prnFileContent "+prnFileContent);
                  navigator.clipboard.readText()
                  .then(text => {
                      var clipRows =text.split(/(\t+)/);
                      console.log(clipRows)
                      $("#sirket").val(clipRows[0]);
                      $("#sirket").prop( "disabled", true );
                      prnFileContent = prnFileContent.replace("sirketismi",iconv.convert(clipRows[0]).toString());

                      $("#urunadi").val(clipRows[2]);
                      $("#urunadi").prop( "disabled", true );
                      prnFileContent = prnFileContent.replace("urunadi",iconv.convert(clipRows[2]).toString());

                      $("#barcode").val(clipRows[4]);
                      $("#barcode").prop( "disabled", true );
                      prnFileContent = prnFileContent.replace("barcode",iconv.convert(clipRows[4]).toString());

                      $("#copy").attr("src","assets/img/paste2.png");
                      $('#print').removeAttr('disabled');
                      $(".badge").css("display","none");


                      prnFileContent = prnFileContent.replace("PQ1","PQ"+$("#count").html() );
                      console.log("prnFileContent "+prnFileContent);

                      $('#count').attr( "id", "countt");
                  })
                  .catch(errx => {
                      console.log('Prn Dosyası Okuma Hatası'+ errx);
                  });

  }


  //yapıştır işlemi yapıldığında readTextFile() fonk. çağırılır
$('body').bind('paste', null, function(e){

    readTextFile(prnfile);

});

//yazdır butonun basıldığında değiştirilen içerikle -prnFileContent- orjinal dosya içeriği değiştirlir
$("#print").on("click",function () {
    printer= $("#printers").val();

barcodewiter= execPath+"\\resources\\app\\assets\\barcode\\barcodeprint.prn";


fs.writeFile(barcodewiter,prnFileContent,{encoding:"latin1"},(error, stdout, stderr)=>{

    if (error) {
        console.log("error"+error);
      }

      if (stderr) {
        console.log("stderr"+stderr);
        return stderr;
      }

});

//sistemde yürütülecek kod yapısı
code='print /D:\\\\%COMPUTERNAME%\\'+printer+" "+barcodewiter;

//kodun yürütülmesi
 barcodecommand= child(code,(error, stdout, stderr)=> {
    if (error) {
        alert("Yazıcıya Erişim Hatası",error);
      }

      if (stderr) {
        alert("stderr"+stderr);
        return stderr;
      }

  });
console.log(barcodecommand);
});

Fonksiyon başlarındaki açıklamaların yeterli olduğu kanısındayım. Zaten yazıda kod açıklamaktan ziyade izlediğim yöntemi açıklamaya çalışıyorum. Fakat dikkat edilmesi gereken noktalar da var şöyle ki;

ZebraDesigner'dan alınan barcode.prn dosyası assets/barcode/ klasörü altında bu bizim etiket dosyamız biz bu dosyanın içeriğini alıp değiştiriyoruz ve yazıcıya tekrar .prn dosyası olarak göndereceğimiz için asset/barcode/ altında barcodeprint.prn dosyası da oluşturmamız gerek. Bu noktada isterseniz barcode.prn dosyasını kopyalayın yada boş bir .prn dosyası oluşturun. Unutmamanız gereken nokta barcodeprint.prn dosyasının içeriğinin sürekli yazdırılan etikete göre farklılaşacağı.

Programı ayağa kaldırmadan önce assets klasörü altında css, js, img ve barcode klasörlerinin varlığını unutmayın yazıda bahsetmediğim resimler ve jquery ve bootstrap dosyları bunların altında.

Ve sıra programı ayağa kaldırmada

npm run start

bu komutla yaptığınız programı ön izleyebilirsiniz.

image.png

Fakat bu durum yeterli değildir. Bunun kurulabilir ve istendiğinde anında başlatılabilir bir program olmasını isteriz tabi ki bunun için 2 adımlı bir işlem yapacağız.

1.Öncelikle pakatleme.

npm run package-win

komutunu girdiğinizde aslında package.json dosyasında tanımlı şu kod yürütülecek ve ana dizininizin üstünde barcodeprint klasörü hemen altında da barcodeprint-win32-x64 oluşturup paketleme işlemini tamamlayacaktır.

"package-win": "electron-packager . barcodeprint --overwrite  --platform=win32 --arch=x64 --icon=./assets/img/print.ico --prune=true --out=../barcodeprint"
  1. adımımız ise dağıtım noktası( şimdilik win için)

Paketlenen yani derlenen programın artık .exe yada .msi şeklinde windows ortamında kurulabilir şekilde dağıtılması gerekmekte.

Bunun için barcodeprint klasörünün olduğu dizinde aşağıdaki dosyayı oluşturuyoruz.


var electronInstaller = require('electron-winstaller');


var settings = {

    appDirectory: './barcodeprint/barcodeprint-win32-x64',

    outputDirectory: './barcodeprintsetup',

    authors: 'Savas HASCELIK',

    exe: './barcodeprint.exe'
};

resultPromise = electronInstaller.createWindowsInstaller(settings);

resultPromise.then(() => {
    console.log("The installers of your application were succesfully created !");
}, (e) => {
    console.log(`Well, sometimes you are not so lucky: ${e.message}`)
});

bu noktadan sonra barcodeprint aşağıdaki komut ile dağıtım işlemini bitiriyoruz.

node build.js

Burada aslında nmp pakati olan electron-winstaller kullanarak .exe oluşturmuş olduk. artık programınızı istediğiniz şekilde dağıtabilirsiniz.

Uyarı node build.js komutunda hata almanız durumunda klasör yollarını veya electron-winstaller paketinin kurulu olup olmadığını muhakkak kontrol edin.

electron-winstaller paketinin kurulumu npm install --save-dev electron-winstaller

Ve setup değikten sonra programınız artık hazır

image.png

image.png

github linki