2013年8月2日

Java 使用 Acrobat Reader 列印 PDF

以 Java 要列印 PDF document 基本上來說並不是什麼大問題,無論是用 AWT PrintJob 或 print service 的 DocPrintJob。

但對於字體/字型的處理,除非買授權的商用套件或統一規範產生 PDF 時的方法,不然想要以 opensource package(ex. Apache PDFBox) 來處理 PDF 中各種造字字體/字型的問題,可以說是一件殺死腦細胞的工作。(雖然我已經幹了)

除此之外,最常見的解決方法是:利用 Windows 平台,以 Acrobat Reader 來印製;好處是,字型檔的處理與前端使用者的 PC 無異。 而程式通常使用 Runtime / Process 或 ProcessBuilder class 來 invoke Acrobat Reader。

不過,依過去發生的案例發現,大體上會發生幾個狀況:
  • 要不要處理 DOS 視窗輸出的訊息
  • 如何取得 return code
  • Acrobat Reader 因發生狀況而未結束 process

解決的策略是:[避免] 發生上述狀況, 取而代之的是去 [控制] 這些狀況
方法是 Java code ----> bat/cmd file ----> Acrobat Reader ;而在 bat/cmd 中加一些基本的處理:
  • 檢查 pdf file 是否存在
  • 檢查 printer (name) 是否存在

例如:
SET PDFFILE=%~1
...
IF NOT EXIST "%PDFFILE%" GOTO PDFNOTFOUND
...
GOTO END

:PDFNOTFOUND
echo file(%PDFFILE%) not found

:END
而檢查 printer 的方法:
rundll32 printui.dll PrintUIEntry /q /Xg /n"{printer name}"

結合上述判斷後再執行 Acrobat Reader 來列印:
"C:\Program Files\Adobe\Reader 10.0\Reader\AcroRd32.exe" /h /o /s /t /n "%PDFFILE%" "{printer name}"

但機車的事發生了,依 AcriRd32.exe 說明而加的參數,理當在執行它之後會結束 bat/cmd ,而回到 Java code 繼續處理;但事實並非如此,無論使用了什麼 cmd.exe 或 start 指令相關參數的功能。

目前唯一測試 OK 的方式是:

  • 在 Java code 運作之前,先啟用 Acrobat Reader 並把它縮小視窗擱在旁邊。

我只能想像的是 Acrobat Reader 是 Windows  program,要控制它除非拿到它的 handle,否則很難與 DOS 視窗的 process 結合,而該 process 只是負責 launch Acrobat Reader 而已。 XD

ps.
(1) Rundll32 printui.dll PrintUIEntry 適用平台 : http://technet.microsoft.com/zh-tw/library/ee624057(v=ws.10).aspx
(2) Apache PDFBox 近年來更新開始頻繁了,好事!

2013年8月1日

SwingBuilder 中佈建 panel

Groovy 使用 SwingBuilder 進行 panel 佈置可有 3 種手法:
(1) 逐一佈建
import groovy.swing.SwingBuilder
import java.awt.*
import javax.swing.*

def swing = new SwingBuilder()

swing.edt {
 frame (
  title: "Ex",
  defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
  pack: true,
  show: true ) {
  
  borderLayout()
  panel(constraints: BorderLayout.CENTER) {
   
   borderLayout()
   panel(constraints: BorderLayout.PAGE_START) {
    label(text: 'header')
   }
   panel(constraints: BorderLayout.CENTER) {
    label(text: 'center 1')
   }
   panel(constraints: BorderLayout.PAGE_END) {
    label(text: 'footer')
   }
   
  }
 }
}
(2) 先簡單的佈建, 接著再佈置主要的 panel (以避免擁擠、縮排過深)
import groovy.swing.SwingBuilder
import java.awt.*
import javax.swing.*

def swing = new SwingBuilder()

swing.edt {
 frame (
  title: "Ex",
  defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
  pack: true,
  show: true ) {
  
  borderLayout()
  panel(constraints: BorderLayout.PAGE_START) {
   
   borderLayout()
   panel(constraints: BorderLayout.PAGE_START) {
    label(text: 'header')
   }
   panel(constraints: BorderLayout.PAGE_END) {
    label(text: 'footer')
   }
   
  }.add(
   panel(constraints: BorderLayout.CENTER) {
    label(text: 'center 2')
   })
 }
}
(3) 先簡單的佈建, 事後再佈置主要的 panel
import groovy.swing.SwingBuilder
import java.awt.*
import javax.swing.*

def swing = new SwingBuilder()
def thePanel

swing.edt {
 frame (
  title: "Ex",
  defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
  pack: true,
  show: true ) {
  
  borderLayout()
  thePanel = panel(constraints: BorderLayout.CENTER) {
   
   borderLayout()
   panel(constraints: BorderLayout.PAGE_START) {
    label(text: 'header')
   }
   panel(constraints: BorderLayout.PAGE_END) {
    label(text: 'footer')
   }
   
  }
...
...
  thePanel.add(
   panel(constraints: BorderLayout.CENTER) {
    label(text: 'center 3')
   })

 }
}

在 SwingBuilder 中建立 CardLayout 的方式

Groovy 透過 SwingBuilder 可以用 DSL 方式表述 swing 各種 components 的佈建。
而建立 CardLayout 有 3 種方式可以進行:
(1) 由 builder 產生
import groovy.swing.SwingBuilder
import java.awt.*
import javax.swing.*

def swing = new SwingBuilder()
def cardLayout = swing.cardLayout()
def thePanel

swing.edt {
 frame (
  title: "Ex",
  defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
  pack: true,
  show: true ) {
  
  borderLayout()
  panel(constraints: BorderLayout.PAGE_START) {
   
   borderLayout()
   panel(constraints: BorderLayout.PAGE_START) {
    label(text: 'header')
   }
   panel(id: 'cards', constraints: BorderLayout.CENTER, layout: cardLayout).with {
    thePanel = it
   }
   panel(constraints: BorderLayout.PAGE_END) {
    label(text: 'footer')
   }
  }

  thePanel.add(
   panel(border: emptyBorder(5,5,5,5)) {
    label(text: 'card 1 label')
   }, 'card1')
  thePanel.add(
   panel(border: emptyBorder(5,5,5,5)) {
    label(text: 'card 2 label')
   }, 'card2')
  cardLayout.show(thePanel, 'card2')
 }
}
(2) new 產生
import groovy.swing.SwingBuilder
import java.awt.*
import javax.swing.*

def swing = new SwingBuilder()
def thePanel

swing.edt {
 frame (
  title: "Ex",
  defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
  pack: true,
  show: true ) {
  
  borderLayout()
  panel(constraints: BorderLayout.PAGE_START) {
   
   borderLayout()
   panel(constraints: BorderLayout.PAGE_START) {
    label(text: 'header')
   }
   panel(id: 'cards', constraints: BorderLayout.CENTER, layout: new CardLayout()).with {
    thePanel = it
   }
   panel(constraints: BorderLayout.PAGE_END) {
    label(text: 'footer')
   }
  }

  thePanel.add(
   panel(border: emptyBorder(5,5,5,5)) {
    label(text: 'card 1 label')
   }, 'card1')
  thePanel.add(
   panel(border: emptyBorder(5,5,5,5)) {
    label(text: 'card 2 label')
   }, 'card2')
  thePanel.getLayout().show(thePanel, 'card2')
 }
}
(3) 由 container 進行 setLayout()
import groovy.swing.SwingBuilder
import java.awt.*
import javax.swing.*

def swing = new SwingBuilder()
def cardLayout
def thePanel

swing.edt {
 frame (
  title: "Ex",
  defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
  pack: true,
  show: true ) {
  
  borderLayout()
  panel(constraints: BorderLayout.PAGE_START) {
   
   borderLayout()
   panel(constraints: BorderLayout.PAGE_START) {
    label(text: 'header')
   }
   panel(id: 'cards', constraints: BorderLayout.CENTER).with {
    setLayout(cardLayout = new CardLayout())
    thePanel = it
   }
   panel(constraints: BorderLayout.PAGE_END) {
    label(text: 'footer')
   }
  }

  thePanel.add(
   panel(border: emptyBorder(5,5,5,5)) {
    label(text: 'card 1 label')
   }, 'card1')
  thePanel.add(
   panel(border: emptyBorder(5,5,5,5)) {
    label(text: 'card 2 label')
   }, 'card2')
  cardLayout.show(thePanel, 'card2')
 }
}
ps. groovy 版本 v2.0.7

2013年7月13日

解決 OSX 10.6 中安裝 SVN 與 Git 的 zlib 問題

對於 Mac 平台的 Java 應用系統開發者來說,安裝與使用 SVN 或 Git 是工作的一環;網路資源有許多資訊可供快速建置,如:How to Install Xcode, Homebrew, Git, RVM, Ruby & Rails on Snow Leopard, Lion, and Mountain Lion。

然而,對我這個 Snow Leopard 用戶來說,就沒這麼順利了;原因在於過去安裝過的 package 歴經硬碟換裝、備份、移除...等,有些開發工作上一些少用的東西不復存在,而且 Apple Developer 網站也不再有與 Snow Leopard 相關的檔案下載資源。直觀上自然會借助 Homebrew 工具來安裝 SVN 或 Git。然而卻出現以下情形:
沒有 zlib 啊~。據了解它就在那些沒有下載支援的 Command Line Tools 中,而早些時候 Xcode 安裝上卻未在預設之列。
索性,open source 還存在,很高興地前往下載:
咦?zlib 下載連結在哪裡?source 路徑還在呀(.../source/zlib/zlib-6),該不會要 file by file 下載吧?!
察看別的 source,路徑規劃的很一致(/source/cvs/cvs-42):
那看看它的下載路徑(.../tarballs/cvs/cvs-42.tar.gz):
就來比照辦理吧!在瀏覧器上輸入 URL:.../tarballs/zlib/zlib-6.tar.gz
成功下載了。接著就解開 zlib 壓縮檔、編譯與安裝:
cd zlib-6
make clean
./configure
make
make install
最後再重新用 brew 安裝 SVN 與 Git:

That's it!

但, 透過 brew 指令裝 subversion 沒有成功:
brew install --universal --java subversion
那只好自己手動解開 cached 下載檔 /Library/Caches/Homebrew/subversion-1.8.0.tar.bz2
接著:
make clean
./configure --prefix=/usr/local/Cellar/subversion/1.8.0 --with-apr=/usr/bin --with-zlib=/usr/local --with-sqlite=/usr/local/opt/sqlite --with-serf=/usr/local/opt/serf --disable-mod-activation --disable-nls --without-apache-libexecdir --without-berkeley-db --enable-javahl --without-jikes
make
make install-javahl
brew unlink subversion
brew link subverion
但不幸的是,v1.8.0 不適用於 OSX 10.6,做了白工。
而接下來的解法有兩種:
(1) 使用人家做好的 package 來安裝。(它會安裝在 /opt 下,而不是一般的 /usr/local 下)
(2) 下載 v.1.6.7 的 formula file:subversion.rb 放到 /usr/local/Library/Formular ;再做一次 brew install

別忘了檢查 /Library/Java/Extensions 下有沒有 libsvnjavahl-1.jnilib 的 symbolic link,沒有的話就自己建立,再設定 IDE (如:eclipse) 的 SVN interface 即完成:

2012年6月6日

用MacBook Pro安裝MacBook小白

常常在網路上爬文,看到關於FireWire火線外接設備比其他USB的貴,總是摸不著道理所在。
手上有一部舊款的MacBook小白,前些日子硬碟掛了,在換上新的之後開始要安裝OSX Tiger;
不過,這是緊張時刻的開始,因為小白的CDROM已老化會有咬片的情形;而且試兩次之後,確實也傷痕累累,深怕手上原版光碟給毀了。
在用功爬文(Melvin1976與BNW)之後,發現可以用FireWire將另一部MacBoo Pro的CDROM,"借"給小白來用;
於是乎照表操課著手進行演練,有圖為證:



















從圖看來,MackBook小白run的是MacBook Pro上的OSX Snow Leopard;
當然接著是選擇CDROM為啟動光碟,重新開機後來進行安裝。
原來~~~FireWire有這樣存在的價值啊!
這次經驗後,也才知道備份安裝光碟有多重要啊!而且使用Disk Utility就可以做了,不用花錢的。