curtis
19小時前 5577f3ba7b9f0319c9b32d7080165207726d1f81
fix: 更新內部引用方法參照
修改47個檔案
新增44個檔案
5012 ■■■■ 已變更過的檔案
CB_IMGPSScanImp.pas 3 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
doc/curtis/Image_Processer_Analysis.md 173 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
doc/curtis/prompt/scanimpl_analysis/04/scanimpl_reAssemble.md 2 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.BusinessLogic.json 21 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.ImageProcessor.json 30 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.ScannerController.json 46 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.TransportManager.json 6 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.UIView.json 284 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
iis_image_process.pas 2712 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/CB_IMGPSScanImp.api.pas 6 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/CB_IMGPSScanImp.docmod.pas 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/CB_IMGPSScanImp.fileOp.pas 14 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/CB_IMGPSScanImp.lfcycle.pas 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/CB_IMGPSScanImp.misc.pas 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/CB_IMGPSScanImp.omr.pas 74 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/CB_IMGPSScanImp.prUpload.pas 6 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/bloc/mermaid/BarCode2CaseID.md 20 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/bloc/mermaid/BarCode2FormID.md 31 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/bloc/mermaid/GetUseCase.md 17 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/bloc/mermaid/SetUseCase.md 17 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/img/mermaid/CheckNeedCrop.md 24 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/img/mermaid/GetSiteOMR.md 43 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/img/mermaid/ImageReSize_FormID.md 41 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/img/transformer.pas 8 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/lfcycle/DestroyEvent.md 13 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/lfcycle/InitialLanguage.md 23 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/lfcycle/Timer1Timer.md 20 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/omr/CheckRule2OMRErrInfo.md 30 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/omr/DistinctFormCode.md 34 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/omr/OMRCheckCase.md 46 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/omr/OMRErr2ini.md 18 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/omr/OMRErrini2List.md 27 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/mermaid/omr/OMRErrini2ListForLog.md 30 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/mermaid/GetDefScanIni.md 37 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/mermaid/OnAcquire.md 60 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/mermaid/PageDone.md 28 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/mermaid/PageEnd.md 46 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/mermaid/R_W_Scanini.md 20 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/mermaid/StatrTwainScan.md 44 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/mermaid/initkscan.md 28 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/scan/twain.pas 14 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/transp/fileClient.pas 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/ActiveFormKeyUp.md 16 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/AddAttFileLBClick.md 21 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/AddScanBtnClick.md 11 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/DesableImage.md 10 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/DisplayMode.md 15 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/DocNoIsExistImg.md 11 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/EnableImage.md 8 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/FindISB2View.md 19 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/GoViewMode.md 14 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/ISB1Click.md 10 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/ISB1ImageMouseDown.md 17 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/ISB1ImageMouseUp.md 13 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/Initialize.md 9 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/NewScanBtnClick.md 13 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/NextPageBtnClick.md 12 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/OptionBtnClick.md 18 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM101Click.md 14 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM102Click.md 14 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM104Click.md 23 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM106Click.md 28 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM108Click.md 25 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM109Click.md 14 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM110Click.md 12 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM111Click.md 17 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM401Click.md 14 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM404Click.md 17 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM505Click.md 10 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM507Click.md 17 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM508Click.md 20 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM601Click.md 21 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM602Click.md 20 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM604Click.md 11 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PM605Click.md 14 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PopupMenu1Popup.md 19 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/PopupMenu6Popup.md 14 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/SampleScanBtnClick.md 21 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/TransBtnClick.md 26 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/ViewMouseMode.md 9 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/mermaid/WNoteBtnClick.md 17 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/misc.pas 54 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/popupMenu.pas 28 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/scrollView.pas 4 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/toolBar.pas 8 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
reassemble/view/treeView.pas 6 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
scripts/dist/CB_IMGPSScanImp.pas.bk.image.csv 13 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
scripts/dist/ErrList.pas.image.csv 5 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
scripts/dist/deps.all.csv 16 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
scripts/list_image_dependencies.js 70 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
scripts/scanimpl_annalysis_json_deps.js 90 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
CB_IMGPSScanImp.pas
@@ -1058,10 +1058,9 @@
{$I reassemble/view/listView.pas}
{$I reassemble/view/msger.pas}
{$I reassemble/scan/twain.pas}
{$I reassemble/img/anchor.pas}
{$I reassemble/CB_IMGPSScanImp.omr.pas}
{$I reassemble/CB_IMGPSScanImp.api.pas}
{$I reassemble/CB_IMGPSScanImp.listMgr.pas}
{$I reassemble/CB_IMGPSScanImp.omr.pas}
{$I reassemble/CB_IMGPSScanImp.fileOp.pas}
{$I reassemble/CB_IMGPSScanImp.docq.pas}
{$I reassemble/img/transformer.pas}
doc/curtis/Image_Processer_Analysis.md
@@ -1,8 +1,8 @@
# 影像處理與轉換模組 (Image Processor) 深度分析
在 `CB_IMGPSScanImp.pas.bk` 中,「影像處理與轉換模組」負責在硬體掃描取得原始影像後,以及上傳/顯示之前,對影像進行一系列的強化、分析與格式轉換。這個模組是決定影像品質、檔案大小與後續 OMR 辨識準確率的關鍵。
在 `CB_IMGPSScanImp.pas.bk` 與 `iis_image_process.pas` 中,「影像處理與轉換模組」負責在硬體掃描取得原始影像後,以及上傳/顯示之前,對影像進行一系列的強化、分析與格式轉換。這個模組是決定影像品質、檔案大小與後續 OMR 辨識準確率的關鍵。
若要將此模組進一步細拆,其內部組成可分為以下四大子分類:
根據源碼分析,其內部組成可細拆為以下六大子分類:
---
@@ -10,113 +10,118 @@
**職責**:專責處理影像的空間座標轉換、旋轉、去斜以及裁切,確保最終儲存的影像端正且符合業務規定的尺寸。
**核心實作特性**:
*   **影像去斜與轉正**:
    *   `DeskewImg`:透過演算法自動偵測影像傾斜角度並將其校正。
    *   `Rotate`:依據條碼辨識出的角度 (`MpsBarcodeinf.r180`) 或使用者手動點擊按鈕 (`SpeedButton14Click` 等),進行 90、180、270 度的旋轉。
*   **影像去斜與轉正 (Deskew & Rotate)**:
    *   `DeskewImg`:透過演算法自動偵測影像傾斜角度並將其校正。支援先以黑白影像偵測角度再套用於彩色影像的優化路徑。
    *   `Rotate`:執行 90、180、270 度的旋轉,可用於條碼方向校正或使用者手動轉向。
*   **幾何校正規範 (Geometric Validation)**:
    *   `GetPosAngle`:計算三個定位點之間的夾角,用以判定影像是否嚴重形變。
    *   `CheckSize`:比對定位點距離與標準長寬,若有巨大落差則記錄並自動執行補償性縮放。
*   **影像裁切與分割**:
    *   `CheckNeedCrop`:根據影像寬高比與偵測到的條碼數量,判斷是否為需要分割的 A3 雙頁合併影像。
    *   `CropImg`:給定 `TRect` 座標,將 A3 影像精準裁切為兩張 A4 (`iGraphic_First`, `iGraphic_sec`)。
    *   `CheckNeedCrop` 與 `CropImg`:針對 A3 雙頁合併影像進行自動分割或特定區域裁切。
*   **影像縮放 (Resize)**:
    *   `ImageReSize_FormID` / `ImageReSize_tmp`:依據表單定義檔 (`FORM_INF`) 中的長寬設定與尋找到的十字定位點,強制將影像縮放變形至標準尺寸,以利後續的 OMR 座標對位。
    *   `DpiResize`:調整影像的 DPI 解析度參數。
    *   `ImageReSize_FormID` / `ImageResize`:依據表單定義或目標解析度強制縮放影像,確保 OMR 座標對位精準。
## 2. 色彩處理與編碼轉換器 (Color & Format Converter)
**職責**:處理影像的色彩深度 (位元深度) 轉換,以及針對不同色彩模式套用最適合的檔案壓縮演算法,以達到最佳的畫質與檔案大小平衡。
**職責**:處理影像色彩深度轉換,套用最適合的壓縮演算法,並進行濾鏡強化以提升辨識率。
**核心實作特性**:
*   **色彩空間轉換**:
    *   `ConvertToBW`:將彩色或灰階影像強制二值化 (Binarization) 轉為黑白影像,這不僅能大幅縮小檔案,也是條碼與 OMR 辨識的必要前置步驟 (系統通常會保留一個隱藏的 `ISB_BW` 物件專做辨識用)。
    *   `ConvertToGray`:將彩色轉換為灰階 (`ifGray256`)。
    *   `Image_Smooth` / `NegativeImg` / `CleanupBorder`:影像平滑化、反相處理與清除黑邊。
*   **格式與壓縮決策**:
    *   針對黑白影像 (`ifBlackWhite`):使用 `tcGroup4` (CCITT Group 4 Fax Compression) 或 `tcPackBits` 壓縮,並儲存為 `.tif`。
    *   針對彩色/灰階影像 (`ifTrueColor`, `ifGray256`, `ifColor256`):轉換為 `TJpegGraphic`,套用 `tcJpeg` 壓縮與指定的壓縮率 (`FJpgCompression` / `SaveQuality`),並儲存為 `.jpg`。
*   **先進二值化 (Advanced Binarization)**:
    *   `ConvertToBW`:標準二值化處理。
    *   `Gray2BW_RTS` / `Color2BW_RTS`:整合 VRS (Virtual ReScan) 技術,透過特定參數精細化灰階轉黑白的過程,保留關鍵特徵。
*   **影像增強濾鏡**:
    *   `Image_Smooth`(平滑)、`Emboss`(浮雕)、`BrightnessImg`(亮度調整)。
    *   `FilterColor`:濾掉特定顏色(如紅字或背景色)僅保留黑白內容。
    *   `CleanupBorder`:自動清除影像邊緣的掃描黑邊。
## 3. 條碼識別與解析器 (Barcode Recognizer)
**職責**:獨立負責掃描與解析影像上的一維或二維條碼,這是系統實現「文件自動歸類」的核心依賴。
**職責**:獨立負責解析影像上的一維或二維條碼,實現「文件自動歸類」。
**核心實作特性**:
*   **條碼引擎呼叫**:
    *   `MpsGetBarcode`:呼叫底層的 MPS Barcode 引擎,傳入二值化的影像 (`ISB_BW.Graphic`)。
    *   回傳 `TMpsBarcodeinf` 結構,包含條碼內容字串陣列 (`Text`) 與每個條碼的方向資訊 (`r180`)。
*   **應用與邏輯綁定**:
    *   透過條碼字串找出對應的 `FormID` 與 `DocNo`。
    *   檢查條碼長度是否符合規範 (`FormIDLength`),過濾雜訊或誤判的條碼。
*   **條碼引擎調用**:
    *   `MpsGetBarcode`:呼叫底層引擎解析二值化影像。
    *   `PrintBarcode`:反向操作,將文字轉換為 Code39 條碼並繪製於影像上。
*   **邏輯綁定**:根據條碼字串自動關聯 `FormID` 與 `DocNo`。
## 4. OMR 與十字定位點分析器 (OMR & Anchor Analyzer)
## 4. OMR 與定位分析引擎 (OMR & Anchor Analyzer)
**職責**:專門為「光學標記辨識 (OMR)」服務,負責找出影像上的基準定位點,並計算特定區域內的黑白像素分佈。
**職責**:為光學標記辨識服務,尋找基準點並計算像素分佈。
**核心實作特性**:
*   **基準點尋找 (Anchor Finding)**:
    *   `FindPoint`:根據 XML 定義的模式 (`ANCHOR` 或 `FRAME`),在影像四個角落尋找定位基準點 (`UpLPoint`, `UpRPoint`, `DownLPoint`, `DownRPoint`)。
    *   `CheckSize`:比對找到的定位點距離與標準長寬是否有巨大落差,若找不到則記錄至 `AnchorError.dat`。
*   **像素計算與標記判定 (Pixel Calculation)**:
    *   `GetSiteOMR`:根據 XML 傳入的相對座標字串 (`Site`),將其換算為實際的 `TRect`。
    *   `Get_OMR`:計算該區域內黑色像素的數量。
    *   結合雜訊過濾 (`ClearLine`) 與容差值 (`SafePixel`, `bt`),最終判定該 OMR 欄位是否「有畫記」。
*   **基準點偵測 (Anchor & Spot Detection)**:
    *   `FindPoint`:支援 `ANCHOR` (十字) 或 `FRAME` (框線) 定位模式。
    *   `FindBlackPoint` / `GetBlackSpots`:在指定邊界內搜尋特定比例的黑塊作為定位參考。
*   **像素判定**:
    *   `Get_OMR`:計算區域內黑色像素數量。
    *   `GetPixBW`:底層像素層級的黑白狀態讀取。
## 5. 影像合成與標記引擎 (Image Synthesis & Annotation) —— [NEW]
**職責**:在影像上疊加業務資訊,或自動生成業務用的單據影像。
**核心實作特性**:
*   **動態浮水印 (Watermark)**:
    *   `Watermark1_Hong`:支援透明度、自定義文字與旋轉(如斜向 330 度)的水印疊加。
*   **業務戳記與單據生成**:
    *   `CreateStamp`:產生包含 UserID 與日期的電子圓戳記。
    *   `CreateNote` / `CreateDraft`:產生空白的便條或草稿圖層。
    *   `CreateReportImg_JSON`:根據 JSON 資料自動合成補件清單影像,整合背景、條碼與文字內容。
## 6. UI 互動輔助與元數據管理 (UI & Metadata) —— [NEW]
**職責**:處理視窗介面上的標記、框選,以及影像檔案內部的隱藏資訊。
**核心實作特性**:
*   **互動標記**:
    *   `ShowKeyinRect` / `SetKeyinRect_New`:在畫面上動態顯示黃色或透明遮罩,引導人員登打特定欄位。
    *   `GetSelectRect2String`:將人員在 UI 上拖曳的框選區域轉換為公分單位。
*   **元數據處理**:
    *   `GetTag` / `SetTag`:利用 Tiff 檔案的 `ImageDescriptionTag` 儲存自定義業務資訊。
    *   `JpgReSize_Exif`:在處理 JPG 影像時保留或讀取拍攝日期等 EXIF 資訊。
---
## 影像相關關鍵字:
```js
[
'TTiffGraphic',
'TDibGraphic',
'DeskewImg',
'Rotate',
'CropImg',
'ImageReSize_FormID',
'ImageReSize_tmp',
'CheckNeedCrop',
'ImageProcessor.transformer',
'ConvertToBW',
'ConvertToGray',
'Image_Smooth',
'NegativeImg',
'CleanupBorder',
'ImageProcessor.converter',
'MpsGetBarcode',
'Get_OMR',
'ImageProcessor.barcodeRecognizer',
'FindPoint',
'BrightnessImg',
'CheckSize',
'GetSiteOMR',
'ImageProcessor.anchorAnalyzer',
'TJpegGraphic',
'DpiResize',
// 以下可省
'SaveQuality',
'FJpgCompression',
'ifTrueColor',
'ifGray256',
'CleanupBorder',
'Color2BW_RTS',
'ConvertToBW',
'ConvertToGray',
'CreateReportImg_JSON',
'CreateStamp',
'CropImg',
'DeskewImg',
'DpiResize',
'FilterColor',
'FindBlackPoint',
'FindPoint',
'GetBlackSpots',
'GetSelectRect2String',
'GetTag',
'Get_OMR',
'Gray2BW_RTS',
'ImageResize',
'Image_Smooth',
'NegativeImg',
'CleanupBorder',
'ifBlackWhite',
'tcGroup4',
'tcPackBits',
'tcJpeg',
'ifColor25'
'JpgReSize_Exif',
'MpsGetBarcode',
'PrintBarcode',
'Rotate',
'SetTag',
'ShowKeyinRect',
'TDibGraphic',
'TJpegGraphic',
'TNBCleanupBorderTransform',
'TTiffGraphic',
'TWatermarkTransform',
'Watermark1_Hong',
]
```
## 💡 未來重構與微服務化建議
若要對此影像處理模組進行重構,建議方向如下:
1.  **抽離為獨立的 Pipeline 模式 (管線模式)**:
    目前這些功能散落在 `OnAcquire` 與各個事件中。應將其重構為一條清晰的影像處理管線:`Image -> Deskew -> Crop -> ConvertBW -> BarcodeRead -> Resize -> Compress -> Save`。每個步驟 (Step) 應該是獨立的類別,方便抽換或開關。
2.  **解耦 UI 與影像處理**:
    目前大量依賴畫面上隱藏的 `ISB_BW` (TImageScrollBox) 來進行二值化和條碼辨識。這違反了 MVC 原則且耗費額外的 GDI 資源。應改用純記憶體物件 (如獨立的 `TDibGraphic` 或 `TBitmap`) 在背景執行緒中進行這些運算,不要綁定可見的 UI 元件。
3.  **引入更現代的影像引擎 (如 OpenCV)**:
    早期依賴的 Envision SDK 在尋找十字定位點 (FindPoint) 和去斜 (DeskewImg) 的演算法可能較為老舊。若未來轉型為微服務架構,可將這部分邏輯移植為 Python/C++ 並使用 OpenCV 來達成更精準的高速運算。
1.  **抽離為獨立的 Pipeline 模式**:
    應將影像處理流程標準化:`Image -> PreProcess -> Recognize -> Synthesize -> Output`。
2.  **Web 化對策**:
    *   **Canvas 合成**:影像合成(水印、戳記)功能可轉移至瀏覽器端的 Canvas API 實作。
    *   **影像處理庫**:OpenCV.js 可用於處理去斜與 OMR 偵測;EXIF.js 用於處理照片元數據。
3.  **解耦 UI 與影像處理**:
    避免依賴 `TImageScrollBox` 等 UI 元件進行二值化運算。改用純記憶體物件處理,以利於在 Web Worker 中平行運算。
doc/curtis/prompt/scanimpl_analysis/04/scanimpl_reAssemble.md
@@ -33,6 +33,8 @@
- ImageProcessor.converter: {{output_path}}/img/converter.pas
- ImageProcessor.anchorAnalyzer: {{output_path}}/img/anchor.pas
- ImageProcessor.payloadArchiver: {{output_path}}/img/imgArchiver.pas
- ImageProcessor.annotation: {{output_path}}/img/annotation.pas
- ImageProcessor.metadata: {{output_path}}/img/metadata.pas
##### 0.5.1.4 tags:BusinessLogic 拆分檔案對應
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.BusinessLogic.json
@@ -102,7 +102,7 @@
    ],
    "lIndex": "5907",
    "rIndex": "5946",
    "description": "向伺服器請求 LASTEST_FORM_INF 最新表單版本資訊(模式 7)。方法會獲取各文件編號對應的最新表單 ID 與版本號,並儲存於 LASTEST_FORM_INF_List。此資訊用於確保使用者掃瞄的是最新版表單,或是用於舊案件轉檔時的版本對照。"
    "description": "向伺服器請求 LASTEST_FORM_INF 最新表單版本資訊(模式 7)。方法會獲取各文件編號對應的最新表單 ID 與版本號,並儲存於 LASTEST_FORM_INF_List。此資訊用於確保使用者掃瞄的是最新版表單,或是用於舊案件轉檔時與歷史版本對照。"
  },
  {
    "matcher": "Function TCB_IMGPSScanX.DeleteDocNoFileForESCAN(Path,DocNo:String):Boolean;  //刪除指定DocNo文件",
@@ -113,8 +113,8 @@
    ],
    "deps": [
      "DeleteDocNoFile",
      "SaveToFile",
      "LoadFromFile",
      "SaveToFile",
      "_DelTree"
    ],
    "lIndex": "6141",
@@ -199,11 +199,11 @@
      "舊案/異動件處理"
    ],
    "deps": [
      "LoadFromFile",
      "FileExists",
      "LoadFromFile",
      "MoveFile",
      "RenameFile",
      "SaveToFile",
      "MoveFile"
      "SaveToFile"
    ],
    "lIndex": "10525",
    "rIndex": "10574",
@@ -275,11 +275,12 @@
      "OMR 檢核"
    ],
    "deps": [
      "ImageReSize_FormID",
      "GetSiteOMR",
      "FindPoint",
      "LoadFromFile",
      "FileExists",
      "FindPoint",
      "GetSiteOMR",
      "ImageReSize_FormID",
      "ImageResize",
      "LoadFromFile",
      "SaveToFile"
    ],
    "lIndex": "12255",
@@ -360,4 +361,4 @@
    "rIndex": "14121",
    "description": "產生案件內自訂文件的彙總資訊字串。方法專門處理代碼開頭為 'ZZZZZ' 的文件目錄,透過 GetCustomDocName 取得使用者定義的文件名稱,並彙整其總份數、總頁數與編輯異動狀態。最終格式與標準文件一致,但首位欄位改為顯示自訂名稱,確保自訂類別的文件也能正確被伺服器識別與儲存。"
  }
]
]
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.ImageProcessor.json
@@ -7,14 +7,14 @@
      "OMR 檢核"
    ],
    "deps": [
      "GetSiteOMR",
      "FindPoint",
      "GetSiteOMR",
      "Get_OMR",
      "LoadFromFile"
    ],
    "lIndex": "5173",
    "rIndex": "5227",
    "description": "在指定影像文件的特定座標執行 OMR (光學標記辨識)。核心邏輯包含:1. 檢查並載入影像檔(若尚未載入),並呼叫 ClearLine 初始化黑白緩衝區。2. 讀取影像的 DPI 與寬高資訊。3. 使用 CM_Str2Rect 將 Site 字串轉為 TRect 矩陣,並參考定位點 (UpLPoint) 進行位移計算。4. 限制辨識區域座標不超出影像邊界。5. 呼叫 Get_OMR 對黑白圖形緩衝區執行辨識並返回結果值(通常為 1 或 0)。此方法是自動化資料檢核的重要工具。"
    "description": "在影像指定座標執行 OMR 辨識。載入影像、計算相對於定位點的座標偏移、限制邊界並呼叫核心辨識程式獲取結果。"
  },
  {
    "matcher": "Procedure TCB_IMGPSScanX.ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放",
@@ -24,17 +24,18 @@
      "影像處理"
    ],
    "deps": [
      "ImageReSize_FormID",
      "FindPoint",
      "CheckSize",
      "LoadFromFile",
      "SaveToFile",
      "FileExists",
      "LoadFileGetMD5"
      "FindPoint",
      "ImageReSize_FormID",
      "ImageResize",
      "LoadFileGetMD5",
      "LoadFromFile",
      "SaveToFile"
    ],
    "lIndex": "6343",
    "rIndex": "6420",
    "description": "根據表單的十字定位點或邊框 (ANCHOR/FRAME) 對掃瞄影像進行縮放調整。流程如下:1. 從 FORM_INF_List 取得表單預設高寬與定位類型。2. 載入對應案件下的影像檔,若為補件模式則跳過原有圖檔。3. 呼叫 FindPoint 尋找影像中的十字線或邊框。4. 呼叫 CheckSize 計算偏移與縮放比例。5. 呼叫 ImageResize 執行實際調整。6. 成功縮放後,記錄檔案 MD5 並儲存更新後的影像,同時將操作記錄寫入 ReSize.dat;若定位點遺失或辨識錯誤,則記錄於 AnchorError.dat。"
    "description": "依據定位點縮放影像。查詢表單規格,載入影像(過濾補件模式),尋找十字線或邊框定位點,執行縮放並紀錄 MD5 與日誌。"
  },
  {
    "matcher": "Procedure TCB_IMGPSScanX.ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)",
@@ -44,14 +45,15 @@
      "影像處理"
    ],
    "deps": [
      "ImageReSize_tmp",
      "CheckSize",
      "ImageReSize_tmp",
      "ImageResize",
      "LoadFromFile",
      "SaveToFile"
    ],
    "lIndex": "6421",
    "rIndex": "6446",
    "description": "針對暫存影像檔案執行十字定位點縮放處理。此方法是 ImageReSize_FormID 的簡化版本,主要針對暫存檔 (FileName) 與指定的 FormID。邏輯包含從資料庫清單查詢表單尺寸與定位模式,若符合 ANCHOR 或 FRAME 類型,則載入影像並呼叫 CheckSize 檢查是否需要調整。若偵測到影像存在偏移或比例差異(SizeStr 非空),則將調整後的影像直接覆蓋儲存回原始路徑。"
    "description": "針對暫存檔執行定位點縮放。簡化版 Resize 邏輯,若偵測到尺寸偏移則直接覆蓋原始檔案,用於處理臨時影像。"
  },
  {
    "matcher": "Function TCB_IMGPSScanX.CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像",
@@ -61,11 +63,11 @@
      "影像處理"
    ],
    "deps": [
      "TDibGraphic",
      "CheckNeedCrop"
      "CheckNeedCrop",
      "TDibGraphic"
    ],
    "lIndex": "9996",
    "rIndex": "10021",
    "description": "判斷掃瞄影像是否為 A3 尺寸並需要進行切割(Crop)。判定邏輯有二:首先,檢查影像寬度是否大於 4 倍的 DPI 閥值,藉此初步判斷為大尺寸掃瞄件;其次,遍歷目前的條碼清單 (MpsBarcodeinf),統計有效的表單代碼 (FormID) 數量。如果影像寬度達標且有效表單數量正好為 2,則返回 True。這代表此張大圖實際上是由兩份 A4 表單併排掃瞄而成,後續需依此旗標進行影像分割處理。"
    "description": "判斷影像是否需執行 A3 切割。依據影像寬度(大於 4 倍 DPI)及條碼清單中有效表單代碼的數量(需為 2 個)作為判定據。"
  }
]
]
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.ScannerController.json
@@ -7,17 +7,17 @@
      "掃描相關"
    ],
    "deps": [
      "TTiffGraphic",
      "StatrTwainScan",
      "Scanner",
      "Scanner.OpenSource",
      "Scanner.CloseSource",
      "OnAcquire",
      "Scanner.AcquireWithSourceOpen"
      "Scanner",
      "Scanner.AcquireWithSourceOpen",
      "Scanner.CloseSource",
      "Scanner.OpenSource",
      "StatrTwainScan",
      "TTiffGraphic"
    ],
    "lIndex": "4390",
    "rIndex": "4443",
    "description": "啟動 TWAIN 掃瞄流程。此方法首先檢查掃瞄器驅動是否已安裝,接著初始化 ScanInfo 結構並設定掃瞄參數(如 DPI、影像格式、是否顯示 UI、雙面掃瞄模式、亮度與對比)。流程中會開啟掃瞄來源,呼叫 AcquireWithSourceOpen 執行實際掃瞄作業,並利用 try...finally 確保不論掃瞄成功與否,最終都會關閉掃瞄來源並釋放資源。"
    "description": "啟動 TWAIN 掃描流程。檢查驅動是否安裝,初始化 ScanInfo 並設定 DPI、影像格式、UI 顯示及雙面掃描模式。執行 AcquireWithSourceOpen 並利用 try...finally 確保資源釋放。"
  },
  {
    "matcher": "procedure TCB_IMGPSScanX.OnAcquire( const DibHandle    : THandle;",
@@ -27,13 +27,13 @@
      "掃描相關"
    ],
    "deps": [
      "TTiffGraphic",
      "OnAcquire",
      "TJpegGraphic",
      "OnAcquire"
      "TTiffGraphic"
    ],
    "lIndex": "4444",
    "rIndex": "4683",
    "description": "掃瞄影像獲取後的回呼處理函數。核心邏輯包含:1. 將獲取的 DIB 句柄轉為影像對象並設定 DPI。2. 依影像格式執行對應處理:黑白影像會進行條碼辨識、依條碼角度旋轉、影像反向、傾斜矯正及清黑邊;全彩或灰階影像則設定 JPEG 壓縮品質。3. 檢查是否需要進行 A3 切割為 A4 的處理。4. 透過 Deletepage 檢查並過濾空白頁(依據檔案大小)。5. 對於有效影像,更新 UI 顯示並呼叫 PageEnd 決定儲存路徑與檔名,最後依副檔名儲存為 TIFF 或 JPEG 並呼叫 PageDone 完成頁面流程。"
    "description": "掃描影像獲取後的回調處理。處理 DIB 句柄、設定 DPI、執行條碼辨識、影像旋轉、反向、去偏斜及清黑邊。支援 A3 裁切判定與空白頁過濾。"
  },
  {
    "matcher": "procedure TCB_IMGPSScanX.PageDone;",
@@ -48,7 +48,7 @@
    ],
    "lIndex": "4684",
    "rIndex": "4733",
    "description": "完成單頁影像處理後的 UI 更新邏輯。此方法會遞增掃瞄影像總計數,並根據目前的掃瞄模式(新建、取代、插入或取樣)將儲存好的影像檔載入到對應的 ImageScrollBox 元件中。在新建模式下,還會根據 ScanImgShowMode 設定決定影像顯示的品質(反鋸齒開啟與否)與縮放模式(zmFittopage),確保使用者能即時預覽掃瞄結果。"
    "description": "單頁影像處理後的 UI 更新。累加計數,根據模式(新建、取代、插入、取樣)將影像載入對應的顯示元件,並依設定調整反鋸齒與縮放。"
  },
  {
    "matcher": "procedure TCB_IMGPSScanX.PageEnd;",
@@ -58,16 +58,16 @@
      "掃描相關"
    ],
    "deps": [
      "GetNoNameCase",
      "DirectoryExists",
      "_DelTree",
      "Str2Dir",
      "GetNoNameCase",
      "PageEnd",
      "SaveToFile",
      "PageEnd"
      "Str2Dir",
      "_DelTree"
    ],
    "lIndex": "4734",
    "rIndex": "4950",
    "description": "管理掃瞄影像的儲存路徑與檔案命名規則。主要邏輯如下:1. 辨識條碼以取得 FormID,並判斷是否為導引頁或分案頁。2. 若偵測到分案頁,會重置計數並嘗試取得新的案件編號(CaseID)。3. 確定儲存目錄,包含處理「分份數」邏輯(依 DocNoNeedDiv 決定是否建立新子目錄)。4. 根據目前頁數與 FormID 產生標準化的檔名(如補零序號_FormID.tif)。5. 更新 ContextList(影像索引資訊)並在掃瞄新案件時更新 TreeView UI 結構。此方法確保每張影像都能正確歸類到對應的案件與文件目錄下。"
    "description": "管理影像儲存路徑與命名。辨識條碼區分表單、導引頁或分案頁;處理分份邏輯與自動建立目錄,並更新樹狀結構索引。"
  },
  {
    "matcher": "Procedure TCB_IMGPSScanX.R_W_Scanini(Mode:Char); //'R'讀取;'W'寫入",
@@ -81,7 +81,7 @@
    ],
    "lIndex": "5254",
    "rIndex": "5295",
    "description": "讀取或寫入掃瞄設定檔(FBScan.ini)。此方法使用 Tinifile 物件處理掃瞄相關參數的 I/O 作業。當 Mode 為 'R' 時,從設定檔讀取包含空白頁刪除設定、影像反向、清黑邊、旋轉角度、傾斜矯正、亮度對比以及 UI 顯示模式等參數;當 Mode 為 'W' 時,則將目前的系統變數值回寫至設定檔中,以利下次啟動時恢復使用者的個性化設定。"
    "description": "讀取或寫入 FBScan.ini。處理包含空白頁刪除、影像反向、清黑邊、旋轉、去偏斜、亮度對比及顯示模式等掃描參數。"
  },
  {
    "matcher": "Procedure TCB_IMGPSScanX.GetDefScanIni; //取得掃瞄的預設值",
@@ -91,13 +91,13 @@
      "掃描相關"
    ],
    "deps": [
      "Rotate",
      "FJpgCompression",
      "Rotate",
      "Scanner"
    ],
    "lIndex": "5296",
    "rIndex": "5472",
    "description": "從資料庫參數清單(WORK_INF_List)初始化並設定掃瞄的系統預設值。流程首先設定一組程式內建的預設數值,隨後遍歷 WORK_INF_List 並比對 PARA_NO 關鍵字,動態更新包含:空白頁判斷大小、影像是否反相、清黑邊、掃瞄 DPI、雙面掃瞄開啟、影像旋轉角度、傾斜矯正、亮度對比、影像儲存路徑、導引頁與分案頁代碼列表、以及 JPEG 壓縮品質等關鍵參數。這使得掃瞄行為可以透過後台設定進行彈性調整。"
    "description": "從資料庫參數清單初始化掃描預設值。設定 DPI、雙面模式、旋轉角度、路徑、導引頁及分案頁代碼等關鍵系統變數。"
  },
  {
    "matcher": "procedure TCB_IMGPSScanX.initkscan;",
@@ -106,13 +106,13 @@
      "ScannerController.twainWrapper"
    ],
    "deps": [
      "initkscan",
      "Scanner",
      "Scanner.CloseSource",
      "Scanner.OpenSource",
      "Scanner.CloseSource"
      "initkscan"
    ],
    "lIndex": "9242",
    "rIndex": "9262",
    "description": "初始化並偵測掃瞄器硬體能力。此方法會先將雙面掃瞄勾選框(ScanDuplexCB)設為停用,接著嘗試開啟掃瞄來源(OpenSource),檢查掃瞄器是否支援雙面掃瞄功能(DuplexCap > 0)。如果硬體支援,則啟用 UI 上的勾選框供使用者選擇。最後確保關閉掃瞄來源,若過程中發生異常,會呼叫 DataLoading(False,True) 停止載入狀態提示。"
    "description": "偵測掃描器硬體能力。嘗試開啟掃描來源以檢查是否支援雙面掃描 (DuplexCap),並據此啟用 UI 控制項。"
  }
]
]
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.TransportManager.json
@@ -73,8 +73,8 @@
      "FTP 相關"
    ],
    "deps": [
      "SetFtpInfo",
      "IIS_Ftp"
      "IIS_Ftp",
      "SetFtpInfo"
    ],
    "lIndex": "11042",
    "rIndex": "11071",
@@ -95,4 +95,4 @@
    "rIndex": "11094",
    "description": "通知後台伺服器 FTP 案件上傳已完成。方法會將包含案件狀態的上傳數據發送至特定的 Servlet 介面。邏輯包含檢查 HTTP 通訊是否成功、解析伺服器回傳的結果代碼,並處理可能的登入過期(Session Timeout)情況。若執行失敗,會將錯誤原因記錄至 HttpErrStr,供後續 UI 顯示錯誤訊息。"
  }
]
]
doc/curtis/prompt/scanimpl_analysis/scanimpl_annalysis.UIView.json
@@ -147,8 +147,8 @@
      "其他內部"
    ],
    "deps": [
      "LoadFromFile",
      "LoadFileGetMD5"
      "LoadFileGetMD5",
      "LoadFromFile"
    ],
    "lIndex": "1338",
    "rIndex": "1362",
@@ -231,9 +231,9 @@
      "UIView.scrollView"
    ],
    "deps": [
      "TJpegGraphic",
      "SaveQuality",
      "SaveToFile"
      "SaveToFile",
      "TJpegGraphic"
    ],
    "lIndex": "1490",
    "rIndex": "1524",
@@ -1017,12 +1017,12 @@
      "UIView.popupMenu"
    ],
    "deps": [
      "FileExists",
      "GetNoNameCase",
      "Str2Dir",
      "LoadFromFile",
      "RenameFile",
      "SaveToFile",
      "FileExists",
      "LoadFromFile"
      "Str2Dir"
    ],
    "lIndex": "1925",
    "rIndex": "1978",
@@ -1068,14 +1068,14 @@
      "UIView.popupMenu"
    ],
    "deps": [
      "CopyFile",
      "DeleteImageFile",
      "DirectoryExists",
      "FileExists",
      "LoadFromFile",
      "DirectoryExists",
      "CopyFile",
      "SaveToFile",
      "DeleteImageFile",
      "ReSortFileName",
      "RenameFile",
      "ReSortFileName"
      "SaveToFile"
    ],
    "lIndex": "2068",
    "rIndex": "2221",
@@ -1088,13 +1088,13 @@
      "UIView.popupMenu"
    ],
    "deps": [
      "Str2Dir",
      "CopyFile",
      "DeleteImageFile",
      "FileExists",
      "LoadFromFile",
      "CopyFile",
      "ReSortFileName",
      "SaveToFile",
      "DeleteImageFile",
      "ReSortFileName"
      "Str2Dir"
    ],
    "lIndex": "2222",
    "rIndex": "2300",
@@ -1108,8 +1108,8 @@
    ],
    "deps": [
      "DeskewImg",
      "SaveToFile",
      "LoadFromFile"
      "LoadFromFile",
      "SaveToFile"
    ],
    "lIndex": "2301",
    "rIndex": "2323",
@@ -1261,9 +1261,9 @@
      "UIView.popupMenu"
    ],
    "deps": [
      "_DelTree",
      "DeleteDocNoFile",
      "DirectoryExists",
      "DeleteDocNoFile"
      "_DelTree"
    ],
    "lIndex": "2573",
    "rIndex": "2677",
@@ -1328,31 +1328,31 @@
      "UIView.popupMenu"
    ],
    "deps": [
      "TTiffGraphic",
      "TJpegGraphic",
      "FJpgCompression",
      "DeskewImg",
      "CheckNeedCrop",
      "ConvertToBW",
      "ConvertToGray",
      "CropImg",
      "DeskewImg",
      "DirectoryExists",
      "FJpgCompression",
      "FileExists",
      "FindFirst",
      "GetNoNameCase",
      "LoadFromFile",
      "MpsGetBarcode",
      "Rotate",
      "CheckNeedCrop",
      "CropImg",
      "ifBlackWhite",
      "tcGroup4",
      "ifColor25",
      "ConvertToGray",
      "tcJpeg",
      "ifTrueColor",
      "ifGray256",
      "SaveQuality",
      "FindFirst",
      "LoadFromFile",
      "SaveToFile",
      "DirectoryExists",
      "_DelTree",
      "GetNoNameCase",
      "Str2Dir",
      "FileExists"
      "TJpegGraphic",
      "TTiffGraphic",
      "_DelTree",
      "ifBlackWhite",
      "ifColor25",
      "ifGray256",
      "ifTrueColor",
      "tcGroup4",
      "tcJpeg"
    ],
    "lIndex": "2789",
    "rIndex": "3186",
@@ -1548,11 +1548,11 @@
      "UIView.popupMenu"
    ],
    "deps": [
      "_DelTree",
      "SaveToFile",
      "ReSortFileName",
      "FileExists",
      "LoadFromFile",
      "FileExists"
      "ReSortFileName",
      "SaveToFile",
      "_DelTree"
    ],
    "lIndex": "3616",
    "rIndex": "3656",
@@ -1684,9 +1684,9 @@
      "UIView.toolBar"
    ],
    "deps": [
      "Scanner",
      "SelectScanner",
      "scanner.SelectScanner",
      "Scanner"
      "scanner.SelectScanner"
    ],
    "lIndex": "4092",
    "rIndex": "4100",
@@ -1733,8 +1733,8 @@
      "UIView.misc"
    ],
    "deps": [
      "FileExists",
      "CopyFile"
      "CopyFile",
      "FileExists"
    ],
    "lIndex": "4165",
    "rIndex": "4194",
@@ -1830,8 +1830,8 @@
      "UIView.misc"
    ],
    "deps": [
      "SetFtpInfo",
      "IIS_Ftp"
      "IIS_Ftp",
      "SetFtpInfo"
    ],
    "lIndex": "4370",
    "rIndex": "4378",
@@ -1844,11 +1844,11 @@
      "UIView.misc"
    ],
    "deps": [
      "SetFtpInfo",
      "FJpgCompression",
      "IIS_Ftp",
      "Rotate",
      "FJpgCompression",
      "Scanner"
      "Scanner",
      "SetFtpInfo"
    ],
    "lIndex": "4379",
    "rIndex": "4389",
@@ -1861,9 +1861,9 @@
      "UIView.misc"
    ],
    "deps": [
      "SaveToFile",
      "FileExists",
      "En_DecryptionStr_Base64",
      "FileExists",
      "SaveToFile",
      "dnFile",
      "dnFile_Get"
    ],
@@ -1878,12 +1878,12 @@
      "UIView.misc"
    ],
    "deps": [
      "Str2Dir",
      "En_DecryptionStr_Base64",
      "FileExists",
      "LoadFromFile",
      "RenameFile",
      "Str2Dir",
      "_DelTree",
      "En_DecryptionStr_Base64",
      "upFile"
    ],
    "lIndex": "5014",
@@ -2111,9 +2111,9 @@
      "檔案操作"
    ],
    "deps": [
      "DeleteImageFile",
      "FileExists",
      "LoadFromFile",
      "DeleteImageFile",
      "ReSortFileName",
      "SaveToFile"
    ],
@@ -2130,10 +2130,10 @@
    ],
    "deps": [
      "DeleteDocNoFile",
      "SaveToFile",
      "ReSortFileName",
      "FileExists",
      "LoadFromFile",
      "FileExists"
      "ReSortFileName",
      "SaveToFile"
    ],
    "lIndex": "6115",
    "rIndex": "6140",
@@ -2213,8 +2213,9 @@
      "影像處理"
    ],
    "deps": [
      "ImageReSize_FormID",
      "FileExists",
      "ImageReSize_FormID",
      "ImageResize",
      "LoadFromFile"
    ],
    "lIndex": "6320",
@@ -2229,8 +2230,8 @@
      "影像處理"
    ],
    "deps": [
      "TDibGraphic",
      "ConvertToBW"
      "ConvertToBW",
      "TDibGraphic"
    ],
    "lIndex": "6447",
    "rIndex": "6456",
@@ -2243,15 +2244,15 @@
      "UIView.misc"
    ],
    "deps": [
      "FileExists",
      "LoadFromFile",
      "CopyFile",
      "FileExists",
      "FindFirst",
      "_DelTree",
      "upFile",
      "SetFtpInfo",
      "FtpCaseComplete",
      "IIS_Ftp",
      "FtpCaseComplete"
      "LoadFromFile",
      "SetFtpInfo",
      "_DelTree",
      "upFile"
    ],
    "lIndex": "6457",
    "rIndex": "6734",
@@ -2343,14 +2344,14 @@
      "UIView.misc"
    ],
    "deps": [
      "FileExists",
      "Str2Dir",
      "SaveToFile",
      "LoadFromFile",
      "DeleteDocNoFile",
      "CopyFile",
      "DeleteDocNoFile",
      "DirectoryExists",
      "En_DecryptionStr_Base64",
      "FileExists",
      "LoadFromFile",
      "SaveToFile",
      "Str2Dir",
      "dnFile"
    ],
    "lIndex": "6815",
@@ -2364,8 +2365,8 @@
      "UIView.misc"
    ],
    "deps": [
      "SetFtpInfo",
      "IIS_Ftp"
      "IIS_Ftp",
      "SetFtpInfo"
    ],
    "lIndex": "6934",
    "rIndex": "6979",
@@ -2378,11 +2379,11 @@
      "UIView.misc"
    ],
    "deps": [
      "En_DecryptionStr_Base64",
      "FileExists",
      "Str2Dir",
      "En_DecryptionStr_Base64",
      "dnFile_Get",
      "dnFile"
      "dnFile",
      "dnFile_Get"
    ],
    "lIndex": "6980",
    "rIndex": "7032",
@@ -2395,8 +2396,8 @@
      "UIView.misc"
    ],
    "deps": [
      "GetNoNameCase",
      "DirectoryExists"
      "DirectoryExists",
      "GetNoNameCase"
    ],
    "lIndex": "7033",
    "rIndex": "7046",
@@ -2409,11 +2410,11 @@
      "UIView.misc"
    ],
    "deps": [
      "LoadFromFile",
      "FileExists",
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile",
      "ReSortFileName"
      "SaveToFile"
    ],
    "lIndex": "7047",
    "rIndex": "7184",
@@ -2428,9 +2429,9 @@
    "deps": [
      "FileExists",
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile",
      "ReSortFileName"
      "SaveToFile"
    ],
    "lIndex": "7185",
    "rIndex": "7306",
@@ -2443,8 +2444,8 @@
      "UIView.misc"
    ],
    "deps": [
      "LoadFromFile",
      "LoadFileGetMD5"
      "LoadFileGetMD5",
      "LoadFromFile"
    ],
    "lIndex": "7307",
    "rIndex": "7351",
@@ -2633,10 +2634,10 @@
      "UIView.misc"
    ],
    "deps": [
      "CopyFile",
      "DirectoryExists",
      "FileExists",
      "LoadFromFile",
      "DirectoryExists",
      "CopyFile",
      "RenameFile",
      "SaveToFile"
    ],
@@ -2729,8 +2730,9 @@
      "UIView.misc"
    ],
    "deps": [
      "LoadFromFile",
      "FileExists",
      "GetTag",
      "LoadFromFile",
      "SaveToFile"
    ],
    "lIndex": "8014",
@@ -2949,8 +2951,8 @@
      "UIView.misc"
    ],
    "deps": [
      "ifGray256",
      "ifBlackWhite",
      "ifGray256",
      "ifTrueColor"
    ],
    "lIndex": "8568",
@@ -3046,9 +3048,9 @@
      "UIView.treeView"
    ],
    "deps": [
      "DirectoryExists",
      "FileExists",
      "LoadFromFile",
      "DirectoryExists"
      "LoadFromFile"
    ],
    "lIndex": "8982",
    "rIndex": "9241",
@@ -3089,10 +3091,10 @@
      "UIView.misc"
    ],
    "deps": [
      "CopyFile",
      "DirectoryExists",
      "FileExists",
      "LoadFromFile",
      "DirectoryExists",
      "CopyFile",
      "SaveToFile"
    ],
    "lIndex": "9376",
@@ -3186,8 +3188,8 @@
    ],
    "deps": [
      "FileExists",
      "dnFile_Get",
      "dnFile",
      "dnFile_Get",
      "upFile"
    ],
    "lIndex": "9825",
@@ -3215,12 +3217,12 @@
      "影像處理"
    ],
    "deps": [
      "FindPoint",
      "DirectoryExists",
      "_DelTree",
      "Str2Dir",
      "FileExists",
      "FindPoint",
      "LoadFromFile",
      "FileExists"
      "Str2Dir",
      "_DelTree"
    ],
    "lIndex": "9932",
    "rIndex": "9995",
@@ -3707,9 +3709,9 @@
    ],
    "deps": [
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile",
      "ReSortFileName"
      "SaveToFile"
    ],
    "lIndex": "11390",
    "rIndex": "11447",
@@ -3723,9 +3725,9 @@
    ],
    "deps": [
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile",
      "ReSortFileName"
      "SaveToFile"
    ],
    "lIndex": "11448",
    "rIndex": "11505",
@@ -3739,13 +3741,13 @@
      "上傳前置資料產生"
    ],
    "deps": [
      "DirectoryExists",
      "_DelTree",
      "Str2Dir",
      "CopyFile",
      "DirectoryExists",
      "FileExists",
      "LoadFromFile",
      "SaveToFile"
      "SaveToFile",
      "Str2Dir",
      "_DelTree"
    ],
    "lIndex": "11547",
    "rIndex": "11672",
@@ -3759,10 +3761,10 @@
      "上傳前置資料產生"
    ],
    "deps": [
      "FileExists",
      "CopyFile",
      "LoadFromFile",
      "DirectoryExists",
      "FileExists",
      "LoadFromFile",
      "SaveToFile"
    ],
    "lIndex": "11673",
@@ -4017,8 +4019,8 @@
      "OMR 檢核"
    ],
    "deps": [
      "LoadFromFile",
      "LoadFileGetMD5"
      "LoadFileGetMD5",
      "LoadFromFile"
    ],
    "lIndex": "12209",
    "rIndex": "12254",
@@ -4075,10 +4077,10 @@
      "影像瀏覽/顯示"
    ],
    "deps": [
      "DirectoryExists",
      "DpiResize",
      "FileExists",
      "LoadFromFile",
      "DirectoryExists"
      "LoadFromFile"
    ],
    "lIndex": "13200",
    "rIndex": "13394",
@@ -4103,11 +4105,11 @@
      "UIView.toolBar"
    ],
    "deps": [
      "Rotate",
      "TJpegGraphic",
      "SaveQuality",
      "LoadFromFile",
      "SaveToFile"
      "Rotate",
      "SaveQuality",
      "SaveToFile",
      "TJpegGraphic"
    ],
    "lIndex": "13400",
    "rIndex": "13422",
@@ -4120,11 +4122,11 @@
      "UIView.toolBar"
    ],
    "deps": [
      "Rotate",
      "TJpegGraphic",
      "SaveQuality",
      "LoadFromFile",
      "SaveToFile"
      "Rotate",
      "SaveQuality",
      "SaveToFile",
      "TJpegGraphic"
    ],
    "lIndex": "13423",
    "rIndex": "13443",
@@ -4137,11 +4139,11 @@
      "UIView.toolBar"
    ],
    "deps": [
      "Rotate",
      "TJpegGraphic",
      "SaveQuality",
      "LoadFromFile",
      "SaveToFile"
      "Rotate",
      "SaveQuality",
      "SaveToFile",
      "TJpegGraphic"
    ],
    "lIndex": "13444",
    "rIndex": "13464",
@@ -4573,9 +4575,9 @@
      "檔案操作"
    ],
    "deps": [
      "ReSortFileName",
      "FileExists",
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile"
    ],
@@ -4591,9 +4593,9 @@
      "檔案操作"
    ],
    "deps": [
      "ReSortFileName",
      "FileExists",
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile"
    ],
@@ -4609,9 +4611,9 @@
      "檔案操作"
    ],
    "deps": [
      "ReSortFileName",
      "FileExists",
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile"
    ],
@@ -4630,8 +4632,8 @@
      "FileExists",
      "LoadFromFile",
      "SaveToFile",
      "dnFile_Get",
      "dnFile"
      "dnFile",
      "dnFile_Get"
    ],
    "lIndex": "14507",
    "rIndex": "14565",
@@ -4648,8 +4650,8 @@
      "FileExists",
      "LoadFromFile",
      "SaveToFile",
      "dnFile_Get",
      "dnFile"
      "dnFile",
      "dnFile_Get"
    ],
    "lIndex": "14566",
    "rIndex": "14624",
@@ -4677,9 +4679,9 @@
      "API 呼叫相關"
    ],
    "deps": [
      "En_DecryptionStr_Base64",
      "FileExists",
      "LoadFromFile",
      "En_DecryptionStr_Base64"
      "LoadFromFile"
    ],
    "lIndex": "14675",
    "rIndex": "14903",
@@ -4694,10 +4696,10 @@
    ],
    "deps": [
      "FJpgCompression",
      "FileExists",
      "GetLocalAppDir",
      "Str2Dir",
      "_DelTree",
      "FileExists",
      "initkscan"
    ],
    "lIndex": "14904",
@@ -4750,13 +4752,13 @@
      "UIView.treeView"
    ],
    "deps": [
      "RenameFile",
      "CopyFile",
      "DeleteImageFile",
      "LoadFromFile",
      "ReSortFileName",
      "RenameFile",
      "SaveToFile",
      "_DelTree",
      "ReSortFileName"
      "_DelTree"
    ],
    "lIndex": "15767",
    "rIndex": "15943",
@@ -5527,4 +5529,4 @@
    "rIndex": "16455",
    "description": "設定倉庫類別(WH_CATEGORY)。此範圍除包含 setter 實作外,亦涵蓋了組件的 initialization 初始區段,負責註冊 ActiveForm 工廠並設定多組授權金鑰(LicenseKey),確保 OCX 運作環境與權限正確初始化。"
  }
]
]
iis_image_process.pas
比對新檔案
@@ -0,0 +1,2712 @@
// 公司 IIS_ImageProcess 相關方法
unit IIS_ImageProcess;
interface
uses EnImgScr,
     EnDiGrph, { for TDibGraphic }
     EnTifGr,  { for TTifGraphic }
     EnJpgGr,  { for TJpegGraphic}
     EnPngGr,  { for TPngGraphic }
     EnTransf, { for TImageTransform }
     EnMisc,   { for MinFloat }
     EnRubber, { for TRubberbandMouseHandler }
     EnPrint,  { for TEnvisionPrintMode, TDibGraphicPrinter }
     DeskewTransform,
     WatermarkTransform,
     mpsBarco,
     NBLib,
     Barcode,
     vrsx,
     dExif,dIPTC,jpeg,
     windows,Graphics,SysUtils,dialogs,classes,iisunit,Printers,
     FindBlackSpots,
     ExtCtrls;
Procedure Rotate(Graphic:TDibGraphic;Angle : Double); //旋轉
Procedure DeskewImg(Graphic : TDibGraphic);Overload; //歪斜矯正
Procedure DeskewImg(Graphic : TDibGraphic;CR_Graphic : TDibGraphic);Overload; //以黑白影像歪斜矯正後再轉彩色影像
Procedure CleanupBorder(Graphic : TDibGraphic); // 清黑邊
procedure NegativeImg(Graphic : TDibGraphic); //反向
procedure ConvertToGray(Graphic : TDibGraphic); //轉成灰階
Function GetTag(FileName :String):String; //將資訊從CustomTag裡取出
Procedure SetTag(FileName,DataStr :String); //將資訊記在CustomTag裡
Procedure CreateStamp(FileName,Title,UserID,StampDate:String;StampWidth,StampHeight:Single); //產生印章
procedure CreateNote(FileName,Title:String); //產生便條紙
procedure CreateDraft(FileName,Title:String); //產生便條紙
Procedure CreateReportImg(FileName,BarCode,Memo,BGFileName:String;DataList,ItemList:TStringlist); //產生補件清單影像
Procedure CreateReportImg_JSON(FileName,BGFileName,BarCode,JSONStr:String);
procedure PrintBarcode(const FileName, Content: WideString; L, T, Width,
      Height: Integer); //印條碼
procedure Watermark(logoBmp:TBitmap;Opacity:Byte;Content:String;Graphic:TDibGraphic);  //讀bmp印在正中央
procedure Watermark1(Opacity:Byte;Content,Content1:String;Graphic:TDibGraphic); //自行產生文字330度印在左上右下
procedure Watermark1_Hong(Opacity:Byte;Content,Content1:String;Graphic:TDibGraphic;ImgH:Integer); //自行產生文字330度印在左上右下
procedure Watermark1_Hong_New(Opacity:Byte;Content,Content1:String;Graphic,DisGraphic:TDibGraphic;ImgH:Integer); //自行產生文字330度印在左上右下
procedure Watermark2(logoBmp:TBitmap;Opacity:Byte;Content:String;Graphic:TDibGraphic);  //讀bmp印在左上方
Procedure FindPoint(Graphic:TDibGraphic;Var UpPointL,UpPointR,DownPointL:TPoint;Var FormWidth,FormHeight:Integer;Anchor:String);overload;
Procedure FindPoint(Graphic:TDibGraphic;Var UpPointL,UpPointR,DownPointL:TPoint;Anchor:String);overload;
function GetBlackSpots(Graphic:TDibGraphic;L, T, R, B, Ratio: Integer): WideString;
Procedure FindBlackPoint(Graphic:TDibGraphic;Var BlackPoint:Tpoint);
Function Get_OMR(Graphic:TDibGraphic;iRect:Trect):integer; //取點數
Function GetSelectRect(ISB:TImageScrollBox):TRect; //取出影像上的框選範圍
Function GetSelectRect_Back(ISB:TImageScrollBox):TRect; //取出影像上的框選範圍(從右下回來)
function GetSelectRect2String(ISB:TImageScrollBox;UpPointL:TPoint): WideString;  //取出影像上的框選範圍轉公分字串
function GetSelectRect_Black2String(ISB:TImageScrollBox;BlackPoint:TPoint): WideString;  //取出影像上的框選範圍從右下小黑框轉公分字串
Procedure SetSelectRect(ISB:TImageScrollBox;iRect:TRect); //顯示出指定的框
Procedure SetSelectRect_Original(ISB:TImageScrollBox;iRect:TRect); //顯示出指定的框(不縮放)
Procedure ShowKeyinRect(ISB:TImageScrollBox;iRect:TRect); //顯示登打位置
procedure ImageResize(Graphic:TDibGraphic;DesWidth,DesHeight:Integer);  //影像縮放
Procedure DpiResize(Graphic:TDibGraphic;DesDpi:Integer;CheckDpi:Boolean); //改變Dpi並依Dpi縮放影像
Procedure DrawPointLine(ISB:TImageScrollBox;UpPointL,UpPointR,DownPointL:TPoint); //畫十字位置
Procedure Gray2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer);Overload;
Procedure Gray2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer;Part:Trect); Overload; //用RTS灰階轉黑白
Procedure Color2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer); Overload; //用RTS彩色轉黑白
Procedure Color2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer;Part:Trect); Overload; //用RTS彩色轉黑白
procedure Emboss(ISB:TImageScrollBox);
Procedure BrightnessImg(ISB:TImageScrollBox;Precent:Integer); //調整亮度
Procedure ConvertToBW(Graphic : TDibGraphic); //轉成黑白
Procedure ConvertTo256Gray(Graphic : TDibGraphic); //轉成256灰階
Procedure ClearLine(Graphic : TDibGraphic;bt:Integer); //清影像上的線條
procedure CropImg(Graphic : TDibGraphic;iRect:TRect); //切範圍影像
Function GetPosAngle(UpL,DownL,UpR:TPoint):Single; //取三點夾角角度
function CheckSize(ISB:TImageScrollBox;UpL,UpR,DownL:TPoint;DefWidth,DefHeight:String): WideString;  //檢查Size並縮放
Function GetPixBW( srcGraphic : TDibGraphic; x,y:integer ):integer;
Procedure BmpConverJpg(Source,SaveFileName:STring); //Bmp轉不壓縮jpeg
Procedure BWTif2Jpg(Graphic:TDibGraphic); //黑白Tif轉彩色jpg
Procedure Color2tif(Graphic:TObject;FileName:String); //彩色影像存Tif
Procedure FieldMask(ISB:TImageScrollBox;SiteList,Mode: WideString;UpPointL:TPoint); //遮罩 Mode:mark mask
procedure SaveAnnotation(ISB:TImageScrollBox;FileName: WideString); //遮罩存檔
Procedure FilterColor(SoISB,DeISB:TImageScrollBox;Ration:Integer); //濾掉顏色 留黑白
procedure JpgReSize_Exif(Maxlength,Quality:integer;OldFile,NewFile:String;WaterGraphic:TGraphic;PrintDate:Boolean); //照片縮放包含Exif
Function GetExif_CaptureDateTime(FileName : String):String; //取出檔案裡的Exif拍攝日期
procedure SetKeyinRect_New( ISB:TImageScrollBox; SiteStr, SiteStr_Black,FormHeight: String; UpPointL,UpPointR:TPoint); overload;
procedure SetKeyinRect_New( ISB:TImageScrollBox; SiteStr, SiteStr_Black,FormHeight: String; UpPointL,UpPointR:TPoint;SP:TShape); overload;
Procedure Image_Smooth(Graphic:TDibGraphic);
procedure PrintImg(FileName, LoginID, Datetime,Path: WideString;WaterBmp:TBitmap); overload;
procedure PrintImg(FileName, LoginID, Datetime,Path: WideString;WaterBmp:TBitmap;Spec_Page:Integer); overload;
procedure PrintImg(FileName, LoginID, Datetime,Path: WideString;WaterBmp:TBitmap;Spec_Page:Integer;NeedSetup:Boolean); overload;
implementation
Procedure Rotate(Graphic:TDibGraphic;Angle : Double);
var
    Transform : TRotateTransform;
    AngleStr  : String;
    FUndoGraphic  : TDibGraphic;
begin
    FUndoGraphic  := TDibGraphic.Create;
    FUndoGraphic.Assign(Graphic);
    Transform := TRotateTransform.Create;
    try
      Transform.Angle           := Angle;
      //Transform.BackgroundColor := MakeRgb(255, 255, 255);
      Transform.ApplyOnDest(FUndoGraphic, Graphic);
    finally
      Transform.Free;
      FUndoGraphic.Free;
    end;
end;
Procedure DeskewImg(Graphic : TDibGraphic);
Var
  Deskew:TDeskewTransform;
begin
  // Create a deskew component
  Deskew:=TDeskewTransform.Create;
  try
    //Deskew.ImageExpansion := True;
    //Deskew.FastEstimation := True;
    // Apply the deskew transform to the image component
    Deskew.Apply(Graphic);
    // Redraw the deskewed image
  finally
  //Destroy the deskew component
  Deskew.Free;
  end;
end;
Procedure DeskewImg(Graphic : TDibGraphic;CR_Graphic : TDibGraphic);Overload; //以黑白影像歪斜矯正後再轉彩色影像
Var
  Deskew:TDeskewTransform;
  Angle : Single;
begin
  // Create a deskew component
  Deskew:=TDeskewTransform.Create;
  try
    //Deskew.ImageExpansion := True;
    //Deskew.FastEstimation := True;
    // Apply the deskew transform to the image component
    Angle := Deskew.DetectSkew(Graphic);
    Deskew.Apply(Graphic);
    Rotate(CR_Graphic,Angle);
    // Redraw the deskewed image
  finally
  //Destroy the deskew component
  Deskew.Free;
  end;
end;
Procedure CleanupBorder(Graphic : TDibGraphic); // 清黑邊
var
    Transform : TNBCleanupBorderTransform;
    FUndoGraphic : TDibGraphic;
begin
  FUndoGraphic := TDibGraphic.Create;
  try
    FUndoGraphic.Assign(Graphic);
    Transform := TNBCleanupBorderTransform.Create;
    try
      Transform.MarginInches := 5;
      Transform.ApplyOnDest(FUndoGraphic, Graphic);
    finally
        Transform.Free;
    end;
  Finally
    FUndoGraphic.Free;
  end;
end;
procedure NegativeImg(Graphic : TDibGraphic);   //反向
var
    Transform : TNegativeTransform;
    FUndoGraphic : TDibGraphic;
begin
 FUndoGraphic := TDibGraphic.Create;
 try
    FUndoGraphic.Assign(Graphic);
    Transform := TNegativeTransform.Create;
    try
        Transform.ApplyOnDest(FUndoGraphic, Graphic);
    finally
        Transform.Free;
    end;
 finally
    FUndoGraphic.Free;
 end;
end;
procedure ConvertToGray(Graphic    : TDibGraphic); //轉灰階
var
    Transform : TConvertToGrayTransform;
    FUndoGraphic : TDibGraphic;
begin
  FUndoGraphic := TDibGraphic.Create;
  try
    FUndoGraphic.Assign(Graphic);
    Transform := TConvertToGrayTransform.Create;
    try
        //Transform.OnProgress := Self.OnProgress;
        Transform.ApplyOnDest(FUndoGraphic, Graphic);
    finally
        Transform.Free;
    end;
  finally
  FUndoGraphic.Free;
  end;
end;
Function GetTag(FileName :String):String; //將資訊從CustomTag裡取出
var
  Tif : TTiffGraphic;
begin
  Result := '';
  if (Uppercase(ExtractFileExt(FileName)) = '.TIF') or (Uppercase(ExtractFileExt(FileName))='.TIFF')  then
  begin
    Tif := TTiffGraphic.Create;
    Try
      Tif.LoadFromFile(FileName);
      IF Tif.ImageDescriptionTag <> '' then
        Result := Tif.ImageDescriptionTag;
    Finally
    Tif.Free;
    end;
  end;
end;
Procedure SetTag(FileName,DataStr :String); //將資訊記在CustomTag裡
var
  Tif : TTiffGraphic;
begin
  if (Uppercase(ExtractFileExt(FileName)) = '.TIF') or (Uppercase(ExtractFileExt(FileName))='.TIFF')  then
  begin
    Tif := TTiffGraphic.Create;
    Try
      Tif.LoadFromFile(FileName);
      Tif.ImageDescriptionTag := DataStr;
      Tif.SaveToFile(FileName);
    Finally
    Tif.Free;
    end;
  end;
end;
Procedure CreateStamp(FileName,Title,UserID,StampDate:String;StampWidth,StampHeight:Single); //產生印章
var i,v:integer;
    bmp:Tbitmap;
    s:string;
    ISB : TImageScrollBox;
    TextH,TextW : Integer;
    X,Y : Integer;
    seg:Integer;
begin
  IF FileExists(FileName) Then
    DeleteFile(FileName);
  //開新影像檔
  bmp := Tbitmap.Create;
  ISB := TImageScrollBox.Create(nil);
  try
    bmp.Width :=  Round(StampWidth/2.54 *300);
    bmp.Height:=  Round(StampHeight/ 2.54 *300);
    bmp.Monochrome := False;
    bmp.Canvas.Font.PixelsPerInch := 300;
    bmp.Canvas.Font.Size := 24;
    bmp.Canvas.Font.Style := [fsBold];
    bmp.Canvas.Font.Name := '標楷體';
    bmp.Canvas.Font.Color := clBlue;
    bmp.Canvas.Brush.Color:= clwhite;
    bmp.Canvas.Rectangle(1,1,bmp.Width,bmp.Height);
    bmp.Canvas.Pen.Width:=24;
    bmp.Canvas.Pen.Color := clblue;
    bmp.Canvas.Rectangle(1,1,bmp.Width,bmp.Height);
    bmp.Transparent := True;
    TextH := bmp.Canvas.TextHeight('退');
    TextW := bmp.Canvas.TextWidth('退');
    Seg := ((bmp.Height - 60 - TextH) - (Length(Title)*TextH)) Div (length(Title)-1);
    X := (Bmp.Width Div 2) - (TextW Div 2);
    Y := 15;
    for i := 1 to length(Title) do
    begin
      bmp.Canvas.TextOut(X,Y,Title[i]);
      if i < length(Title) then
        Y := Y + seg +(TextH)
      else
        Y := Y + TextH;
    end;
    bmp.Canvas.Font.Size := 10;
    bmp.Canvas.Font.Name := '標楷體';
    TextW := bmp.Canvas.TextWidth(GetDate);
    bmp.Canvas.TextOut(((bmp.Width) div 2)-(TextW div 2),Y+20,StampDate);
    Y := Y + 60;
    bmp.Canvas.Font.Size := 10;
    bmp.Canvas.Font.Name := '標楷體';
    TextW := bmp.Canvas.TextWidth(UserID);
    bmp.Canvas.TextOut((bmp.Width div 2)-(TextW div 2),Y+20,UserID);
    {if Title <> '' then
    begin
      TextW := bmp.Canvas.TextWidth(Title);
      bmp.Canvas.TextOut((bmp.Width div 2) - (TextW div 2) ,Round(3*300) ,Title );
    end;}
    bmp.SaveToFile(ExtractFilePath(FileName)+'tmp.bmp');
    //ISB.Graphic.Canvas.Assign(bmp.Canvas);
    ISB.LoadFromFile(ExtractFilePath(FileName)+'tmp.bmp',1);
    DeleteFile(ExtractFilePath(FileName)+'tmp.bmp');
    //showmessage(ViewPath+'note.bmp');
    ISB.Graphic.XDotsPerInch:=300;
    ISB.Graphic.YDotsPerInch:=300;
    ISB.SaveToFile(FileName);
  finally
  bmp.Free;
  ISB.Free;
  end;
end;
procedure CreateNote(FileName,Title:String); //產生便條紙
var i,v:integer;
    bmp:Tbitmap;
    s:string;
    ISB : TImageScrollBox;
    TextW : Integer;
begin
  IF FileExists(FileName) Then
    DeleteFile(FileName);
  //開新影像檔
  bmp := Tbitmap.Create;
  ISB := TImageScrollBox.Create(nil);
  try
    bmp.Width :=  8 * 300;
    bmp.Height:=  11 * 300;
    bmp.Monochrome := true;
    bmp.Canvas.Font.Size := 60;
    bmp.Canvas.Brush.Color:= clwhite;
    bmp.Canvas.Rectangle(1,1,bmp.Width,bmp.Height);
    if Title <> '' then
    begin
      TextW := bmp.Canvas.TextWidth(Title);
      bmp.Canvas.TextOut((bmp.Width div 2) - (TextW div 2) ,Round(3*300) ,Title );
    end;
    bmp.SaveToFile(ExtractFilePath(FileName)+'tmp.bmp');
    //ISB.Graphic.Canvas.Assign(bmp.Canvas);
    ISB.LoadFromFile(ExtractFilePath(FileName)+'tmp.bmp' ,1);
    DeleteFile(ExtractFilePath(FileName)+'tmp.bmp');
    //showmessage(ViewPath+'note.bmp');
    ISB.Graphic.XDotsPerInch:=300;
    ISB.Graphic.YDotsPerInch:=300;
    ISB.SaveToFile(FileName);
  finally
  bmp.Free;
  ISB.Free;
  end;
end;
procedure CreateDraft(FileName,Title:String); //產生便條紙
var i,v:integer;
    bmp:Tbitmap;
    s:string;
    ISB : TImageScrollBox;
    TextW : Integer;
begin
  IF FileExists(FileName) Then
    DeleteFile(FileName);
  //開新影像檔
  bmp := Tbitmap.Create;
  ISB := TImageScrollBox.Create(nil);
  try
    bmp.Width :=  8 * 300;
    bmp.Height:=  11 * 300;
    bmp.Monochrome := False;
    bmp.Canvas.Font.Size := 60;
    bmp.Canvas.Brush.Color:= clwhite;
    bmp.Canvas.Rectangle(1,1,bmp.Width,bmp.Height);
    if Title <> '' then
    begin
      TextW := bmp.Canvas.TextWidth(Title);
      bmp.Canvas.TextOut((bmp.Width div 2) - (TextW div 2) ,Round(0.5*300) ,Title );
    end;
    bmp.SaveToFile(ExtractFilePath(FileName)+'tmp.bmp');
    //ISB.Graphic.Canvas.Assign(bmp.Canvas);
    ISB.LoadFromFile(ExtractFilePath(FileName)+'tmp.bmp' ,1);
    DeleteFile(ExtractFilePath(FileName)+'tmp.bmp');
    //showmessage(ViewPath+'note.bmp');
    ISB.Graphic.XDotsPerInch:=300;
    ISB.Graphic.YDotsPerInch:=300;
    ISB.SaveToFile(FileName);
  finally
  bmp.Free;
  ISB.Free;
  end;
end;
procedure PrintBarcode(const FileName, Content: WideString; L, T,
  Width, Height: Integer);
Var
  Rect : TRect;
  AsBarCode : TAsBarCode;
  ISB : TImageScrollBox;
  TextW : Integer;
begin
  AsBarCode := TAsBarCode.Create(nil);
  ISB := TImageScrollBox.Create(nil);
  try
    Rect.Left := L-40;
    Rect.Top := T-40;
    Rect.Right := L+Width+40;
    Rect.Bottom := T+Height+40;
    ISB.LoadFromFile(FileName,1);
    ISB.Graphic.Canvas.Brush.Color := clWhite;
    ISB.Graphic.Canvas.FillRect(Rect);
    AsBarcode.Typ :=  bcCode39;
    AsBarCode.ShowText := bcoNone;
    AsBarcode.Text := Content;
    AsBarcode.Left := L;
    AsBarcode.Top := T;
    AsBarcode.Width := Width;
    AsBarcode.Height := Height;
    AsBarCode.DrawBarcode(ISB.Graphic.Canvas);
    ISB.Graphic.Canvas.Font.Size := 28;
    TextW := ISB.Graphic.Canvas.TextWidth(Content);
    AsBarcode.Left := L + Round(Width Div 2)-(Textw div 2);
    AsBarcode.Top := T + Height;
    //AsBarcode.Top := Round(0.1/2.54*ISB.Graphic.XDotsPerInch);
    AsBarCode.ShowText := bcoCode;
    AsBarCode.DrawText(ISB.Graphic.Canvas);
    ISB.SaveToFile(FileName);
  finally
    AsBarCode.Free;
    ISB.Free;
  end;
end;
Procedure CreateReportImg(FileName,BarCode,Memo,BGFileName:String;DataList,ItemList:TStringlist); //產生補件清單影像
var i,v,v1:integer;
    bmp:Tbitmap;
    s:string;
    ISB,ISB1 : TImageScrollBox;
    TextW,TextH,NowX,NowY : Integer;
    MemoList : TStringlist;
    DRect,SRect:TRect;
begin
  IF FileExists(FileName) Then
    DeleteFile(FileName);
  //開新影像檔
  bmp := Tbitmap.Create;
  MemoList := TStringlist.Create;
  ISB := TImageScrollBox.Create(nil);
  ISB1 := TImageScrollBox.Create(nil);
  try
    bmp.Width :=  8 * 300;
    bmp.Height:=  11 * 300;
    bmp.Monochrome := False;
    bmp.Canvas.Font.Size := 24;
    bmp.Canvas.Font.Name := '標楷體';
    bmp.Canvas.Brush.Color:= clwhite;
    bmp.Canvas.Rectangle(1,1,bmp.Width,bmp.Height);
    TextW := bmp.Canvas.TextWidth('測試');
    TextH := bmp.Canvas.TextHeight('測試');
    NowY := 600;
    NowX := bmp.Width Div 2;
    for i := 0 to DataList.Count - 1 do
    begin
      NowX := ((bmp.Width Div 2) * (i mod 2)) + 100;
      bmp.Canvas.TextOut(NowX,NowY,DataList.Strings[i]);
      NowY := NowY+ ((TextH * (i mod 2))+ (100*(i mod 2)));
    end;
    NowY := NowY +TextH+ 100;
    bmp.Canvas.TextOut(100,NowY,'說明:');
    NowY := NowY + TextH+20;
    v:=1;
    v1:=1;
    for i := 1 to Length(Memo) do  //計算要換行字數
    begin
      TextW := bmp.Canvas.TextWidth(Copy(Memo,v,v1));
      if TextW >= (bmp.Width - 250) then
      begin
        MemoList.Add(Copy(Memo,v,v1));
        v := i+1;
        v1:=0;
      end;
      inc(v1);
    end;
    if v <= length(Memo) then
      MemoList.Add(Copy(Memo,v,length(Memo)));
    for i := 0 to MemoList.Count - 1 do
    begin
      bmp.Canvas.TextOut(150,NowY,MemoList.Strings[i]);
      NowY := NowY+TextH+20;
    end;
    //TextW := bmp.Canvas.TextHeight(Memo);
    //bmp.Canvas.TextOut(150,NowY,Memo);
    NowY := NowY + 100;
    bmp.Canvas.TextOut(100,NowY,'補件項目:');
    NowY := NowY +TextH+20;
    for i := 0 to ItemList.Count - 1 do
    begin
      bmp.Canvas.TextOut(150,NowY,ItemList.Strings[i]);
      NowY := NowY+TextH+50;
    end;
    bmp.SaveToFile(ExtractFilePath(FileName)+'tmp.bmp');
    PrintBarcode(ExtractFilePath(FileName)+'tmp.bmp',BarCode,150,150,1600,150);
    //ISB.Graphic.Canvas.Assign(bmp.Canvas);
    ISB.LoadFromFile(ExtractFilePath(FileName)+'tmp.bmp' ,1);
    DeleteFile(ExtractFilePath(FileName)+'tmp.bmp');
    ISB1.LoadFromFile(BGFileName,1);
    Srect := Rect(1,1,ISB.Graphic.Width,ISB.Graphic.Height);
    DRect := Rect(1,1,ISB1.Graphic.Width,ISB1.Graphic.Height);
    ISB1.Graphic.Canvas.CopyMode := SRCAND;
    ISB1.Graphic.Canvas.CopyRect(DRect,ISB.Graphic.Canvas,SRect);
    ISB1.Graphic.XDotsPerInch:=300;
    ISB1.Graphic.YDotsPerInch:=300;
    ISB1.SaveToFile(FileName);
    //showmessage(ViewPath+'note.bmp');
    {ISB.Graphic.XDotsPerInch:=300;
    ISB.Graphic.YDotsPerInch:=300;
    ISB.SaveToFile(FileName);}
  finally
  bmp.Free;
  ISB.Free;
  ISB1.Free;
  MemoList.Free;
  end;
end;
Procedure CreateReportImg_JSON(FileName,BGFileName,BarCode,JSONStr:String);
var i,n,v,v1:integer;
    bmp:Tbitmap;
    s:string;
    ISB,ISB1 : TImageScrollBox;
    TextW,TextH,NowX,NowY : Integer;
    MemoList,ItemList,PrtList : TStringlist;
    DRect,SRect:TRect;
begin
  IF FileExists(FileName) Then
    DeleteFile(FileName);
  //開新影像檔
  bmp := Tbitmap.Create;
  MemoList := TStringlist.Create;
  PrtList := TStringlist.Create;
  ItemList := TStringlist.Create;
  ISB := TImageScrollBox.Create(nil);
  ISB1 := TImageScrollBox.Create(nil);
  try
    bmp.Width :=  8 * 300;
    bmp.Height:=  11 * 300;
    bmp.Monochrome := False;
    bmp.Canvas.Font.PixelsPerInch := 300;
    bmp.Canvas.Font.Size := 12;
    bmp.Canvas.Font.Name := '標楷體';
    bmp.Canvas.Brush.Color:= clwhite;
    bmp.Canvas.Rectangle(1,1,bmp.Width,bmp.Height);
    TextW := bmp.Canvas.TextWidth('測試');
    TextH := bmp.Canvas.TextHeight('測試');
    NowY := 600;
    NowX := bmp.Width Div 2;
    ////////Title///////
    PrtList := Getjsonlist(JSONStr,'Title/Content');
    for i := 0 to PrtList.Count - 1 do
    begin
      TextW := bmp.Canvas.TextWidth(PrtList.Strings[i]);
      NowX := 300;
      if UpperCase(Getjsondata(JSONStr,'Title/Align')) = 'CENTER' Then
        NowX := (bmp.Width Div 2) - (TextW Div 2);
      bmp.Canvas.TextOut(NowX,NowY,PrtList.Strings[i]);
      NowY := NowY +TextH+ 20;
    end;
    ////////Prefix///////
    NowY := NowY + 80;
    PrtList := Getjsonlist(JSONStr,'Prefix/Content');
    for i := 0 to PrtList.Count - 1 do
    begin
      TextW := bmp.Canvas.TextWidth(PrtList.Strings[i]);
      NowX := 300;
      if UpperCase(Getjsondata(JSONStr,'Prefix/Align')) = 'CENTER' Then
        NowX := (bmp.Width Div 2) - (TextW Div 2);
      bmp.Canvas.TextOut(NowX,NowY,PrtList.Strings[i]);
      NowY := NowY +TextH+ 20;
    end;
    ////////Data/////////
    NowY := NowY + 80;
    ItemList := GetJsonObjList(JSONStr,'Data/Content');
    for i := 0 to ItemList.Count - 1 do
    begin
      NowX := 300;
      TextW := bmp.Canvas.TextWidth(ItemList.Strings[i]);
      if UpperCase(Getjsondata(JSONStr,'Data/Align')) = 'CENTER' Then
        NowX := (bmp.Width Div 2) - (TextW Div 2);
      bmp.Canvas.TextOut(NowX,NowY,ItemList.Strings[i]);
      NowX := NowX +TextW;
      PrtList := Getjsonlist(JSONStr,'Data/Content/'+ItemList.Strings[i]);
      for n := 0 to PrtList.Count - 1 do
      begin
        TextW := bmp.Canvas.TextWidth(PrtList.Strings[n]);
        bmp.Canvas.TextOut(NowX,NowY,PrtList.Strings[n]);
        NowY := NowY +TextH+ 20;
      end;
    end;
    ///////Memo////////
    NowY := NowY + 80;
    PrtList := Getjsonlist(JSONStr,'Memo/Content');
    for i := 0 to PrtList.Count - 1 do
    begin
      TextW := bmp.Canvas.TextWidth(PrtList.Strings[i]);
      NowX := 300;
      if UpperCase(Getjsondata(JSONStr,'Memo/Align')) = 'CENTER' Then
        NowX := (bmp.Width Div 2) - (TextW Div 2);
      bmp.Canvas.TextOut(NowX,NowY,PrtList.Strings[i]);
      NowY := NowY +TextH+ 20;
    end;
    ///////Suffix///////
    NowY := NowY + 80;
    PrtList := Getjsonlist(JSONStr,'Suffix/Content');
    for i := 0 to PrtList.Count - 1 do
    begin
      TextW := bmp.Canvas.TextWidth(PrtList.Strings[i]);
      NowX := 300;
      if UpperCase(Getjsondata(JSONStr,'Suffix/Align')) = 'CENTER' Then
        NowX := (bmp.Width Div 2) - (TextW Div 2);
      bmp.Canvas.TextOut(NowX,NowY,PrtList.Strings[i]);
      NowY := NowY +TextH+ 20;
    end;
    bmp.SaveToFile(ExtractFilePath(FileName)+'tmp.bmp');
    PrintBarcode(ExtractFilePath(FileName)+'tmp.bmp',BarCode,150,150,1600,150);
    //ISB.Graphic.Canvas.Assign(bmp.Canvas);
    ISB.LoadFromFile(ExtractFilePath(FileName)+'tmp.bmp' ,1);
    DeleteFile(ExtractFilePath(FileName)+'tmp.bmp');
    ISB1.LoadFromFile(BGFileName,1);
    Srect := Rect(1,1,ISB.Graphic.Width,ISB.Graphic.Height);
    DRect := Rect(1,1,ISB1.Graphic.Width,ISB1.Graphic.Height);
    ISB1.Graphic.Canvas.CopyMode := SRCAND;
    ISB1.Graphic.Canvas.CopyRect(DRect,ISB.Graphic.Canvas,SRect);
    ISB1.Graphic.XDotsPerInch:=300;
    ISB1.Graphic.YDotsPerInch:=300;
    ISB1.SaveToFile(FileName);
    //showmessage(ViewPath+'note.bmp');
    {ISB.Graphic.XDotsPerInch:=300;
    ISB.Graphic.YDotsPerInch:=300;
    ISB.SaveToFile(FileName);}
  finally
  bmp.Free;
  ISB.Free;
  ISB1.Free;
  MemoList.Free;
  PrtList.Free;
  ItemList.Free;
  end;
end;
procedure Watermark(logoBmp:TBitmap;Opacity:Byte;Content:String;Graphic:TDibGraphic);
Var WatermarkTransform:TWatermarkTransform;
    WatermarkImage:TDibGraphic;
    WatermarkImage1:TDibGraphic;
    S : String;
    PW,PH : Integer;
    x,y : Integer;
    WH,WW : Integer;
    destrect,sourRect : Trect;
    WaterH,WaterW : Integer;
begin
   // Allows user to select a file (the watermark)
   // Create the graphics specific for the selected extension
   WatermarkImage1:=TDibGraphic.Create;
   WatermarkImage:=TDibGraphic.Create;
   try
    // Load the watermark
    WatermarkImage1.Assign(logoBmp);
    IF (Graphic.Height > Graphic.Width) Then
    begin
      WaterW := (Graphic.Width div 3);
      WaterH := Round( WatermarkImage1.Height * (WaterW / WatermarkImage1.Width));
    end
    Else
    begin
      WaterH := (Graphic.Height div 3);
      WaterW := Round( WatermarkImage1.Width * (WaterH / WatermarkImage1.Height));
    end;
    WatermarkImage.NewImage(WaterW,WaterH+30,ifTrueColor, nil, 0, 0);
    WatermarkImage.Canvas.Brush.Color := clWhite;
    WatermarkImage.Canvas.FillRect(Rect(0,0,WatermarkImage.width,WatermarkImage.Height));
    // Create the watermark transform
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage1.Width;
    sourrect.Bottom := WatermarkImage1.Height;
    destrect.Left := 0;
    destrect.Top := 0;
    destrect.Right := WaterW;
    destrect.Bottom := WaterH;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
    WH := Graphic.Height div 2;
    WW := Graphic.Width div 2;
    WatermarkTransform:=TWatermarkTransform.Create;
    try
      // Set the watermark to use
      WatermarkTransform.Watermark:=WatermarkImage;
      // Select the position where place the watermark: centered !
      WatermarkTransform.Position:=Point(WW -(WatermarkImage.Width div 2),
                                        WH -(WatermarkImage.Height div 2));
      //WatermarkTransform.Position:=Point((ImageScrollBox.DisplayedGraphic.Width-WatermarkImage.Width) div 2,
        //                                 (ImageScrollBox.DisplayedGraphic.Height-WatermarkImage.Height) div 2);
      IF Content <> '' Then
      begin
        WatermarkTransform.Watermark.Canvas.Font.Style := [fsBold];
        WatermarkTransform.Watermark.Canvas.Font.Size := 36;
        S := Content;
        PW := WatermarkTransform.Watermark.Canvas.TextWidth(Content);
        PH := WatermarkTransform.Watermark.Canvas.TextHeight(Content);
        WatermarkTransform.Watermark.Canvas.TextOut((WatermarkTransform.Watermark.Width - PW) Div 2,WatermarkTransform.Watermark.Height-PH-10,Content);
      end;
      // Set the opacity %
      WatermarkTransform.Opacity:=Opacity;
      // Apply the watermark
      WatermarkTransform.Apply(Graphic);
      //ImageScrollBox.Redraw(False);
      //ShowMessage('Watermark applied at image center with 25% opacity !');
    finally
     WatermarkTransform.Free;
    end;
  finally
    WatermarkImage.Free;
    WatermarkImage1.Free;
   end;
end;
procedure Watermark1(Opacity:Byte;Content,Content1:String;Graphic:TDibGraphic);
Var WatermarkTransform:TWatermarkTransform;
    WatermarkImage:TDibGraphic;
    WatermarkImage1:TDibGraphic;
    WatermarkImage2:TDibGraphic;
    destrect,sourRect : Trect;
    WaterH,WaterW : Integer;
    Text_H,Text_W : Integer;
begin
   // Allows user to select a file (the watermark)
   // Create the graphics specific for the selected extension
   if Content = '' then Exit;
   WatermarkImage1:=TDibGraphic.Create;
   WatermarkImage2:=TDibGraphic.Create;
   WatermarkImage:=TDibGraphic.Create;
   try
    // Load the watermark
    //WatermarkImage1.Assign(logoBmp);
    Graphic.Canvas.Font.Size := 20;
    Text_W := Graphic.Canvas.TextWidth(Content)+80;
    Text_H := Graphic.Canvas.TextHeight(Content);
    WaterW := Text_W;
    WaterH := Text_W div 6;
    WatermarkImage1.NewImage(WaterW,WaterH,ifTrueColor, nil, 0, 0);
    WatermarkImage1.Canvas.Brush.Color := clWhite;
    WatermarkImage1.Canvas.FillRect(Rect(0,0,WatermarkImage1.width,WatermarkImage1.Height));
    WatermarkImage1.Canvas.Font.Size := 20;
    WatermarkImage1.Canvas.Font.Style := [fsBold];
    WatermarkImage1.Canvas.Font.Name := 'Times New Roman';
    WatermarkImage1.Canvas.TextOut(1,WaterH div 2,Content);
    WatermarkImage1.Canvas.TextOut(1,WaterH div 12,content1);
    Rotate(WatermarkImage1,330);  //轉330度
    WatermarkImage2.NewImage(WaterW,WaterH,ifTrueColor, nil, 0, 0);
    WatermarkImage2.Canvas.Brush.Color := clWhite;
    WatermarkImage2.Canvas.FillRect(Rect(0,0,WatermarkImage2.width,WatermarkImage2.Height));
    WatermarkImage2.Canvas.Font.Size := 20;
    WatermarkImage2.Canvas.Font.Style := [fsBold];
    WatermarkImage2.Canvas.Font.Name := 'Times New Roman';
    WatermarkImage2.Canvas.TextOut(1,WaterH div 2,Content);
    WatermarkImage2.Canvas.TextOut(1,WaterH div 12,content1);
    Rotate(WatermarkImage2,225);  //轉330度
    WatermarkImage.NewImage(Graphic.Width,Graphic.Height,ifTrueColor, nil, 0, 0);
    WatermarkImage.Canvas.Brush.Color := clWhite;
    WatermarkImage.Canvas.FillRect(Rect(0,0,WatermarkImage.width,WatermarkImage.Height));
    // Create the watermark transform
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage1.Width;
    sourrect.Bottom := WatermarkImage1.Height;
    destrect.Left := 0;
    destrect.Top := 0;
    destrect.Right := WatermarkImage1.Width;
    destrect.Bottom := WatermarkImage1.Height;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage2.Width;
    sourrect.Bottom := WatermarkImage2.Height;
    destrect.Left := 0;
    destrect.Top := WatermarkImage.Height-(WatermarkImage2.Height * 3 div 2);
    destrect.Right := WatermarkImage2.Width;
    destrect.Bottom := WatermarkImage.Height-WatermarkImage2.Height div 2;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage2.Canvas,sourrect);
    {destrect.Left := WatermarkImage.Width-WatermarkImage1.Width;
    destrect.Top := WatermarkImage.Height-WatermarkImage1.Height;
    destrect.Right := WatermarkImage.Width;
    destrect.Bottom := WatermarkImage.Height;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
     }
    WatermarkTransform:=TWatermarkTransform.Create;
    try
      // Set the watermark to use
      WatermarkTransform.Watermark:=WatermarkImage;
      // Select the position where place the watermark: centered !
      WatermarkTransform.Position:=Point(80,80);
      //WatermarkTransform.Position:=Point((ImageScrollBox.DisplayedGraphic.Width-WatermarkImage.Width) div 2,
        //                                 (ImageScrollBox.DisplayedGraphic.Height-WatermarkImage.Height) div 2);
      // Set the opacity %
      WatermarkTransform.Opacity:=Opacity;
      // Apply the watermark
      WatermarkTransform.Apply(Graphic);
      //ImageScrollBox.Redraw(False);
      //ShowMessage('Watermark applied at image center with 25% opacity !');
    finally
     WatermarkTransform.Free;
    end;
  finally
    WatermarkImage.Free;
    WatermarkImage1.Free;
    WatermarkImage2.Free;
   end;
end;
procedure Watermark1_Hong(Opacity:Byte;Content,Content1:String;Graphic:TDibGraphic;ImgH:Integer);
Var WatermarkTransform:TWatermarkTransform;
    WatermarkImage:TDibGraphic;
    WatermarkImage1:TDibGraphic;
    WatermarkImage2:TDibGraphic;
    destrect,sourRect : Trect;
    WaterH,WaterW : Integer;
    Text_H,Text_W : Integer;
    i,pre : Integer;
    Zoompercent : Single;
    Water_SP : Integer;
    Constr : String;
begin
   // Allows user to select a file (the watermark)
   // Create the graphics specific for the selected extension
   if Content = '' then Exit;
   Constr := Content;
   if length(Content1) > length(Content) then
     Constr := Content1;
   //Showmessage(inttostr(ImgH)+#13+inttostr(Graphic.Height)+#13+inttostr(Graphic.XDotsPerInch));
   Zoompercent := Graphic.Height/ImgH *100;
   //Showmessage(floattostr(Zoompercent));
   WatermarkImage1:=TDibGraphic.Create;
   WatermarkImage2:=TDibGraphic.Create;
   WatermarkImage:=TDibGraphic.Create;
   try
    // Load the watermark
    //WatermarkImage1.Assign(logoBmp);
    //showmessage(inttostr(Graphic.Width)+#13+inttostr(Graphic.Height));
    Graphic.Canvas.Font.Style := [fsBold];
    Graphic.Canvas.Font.Name := '標楷體';
    Graphic.Canvas.Font.PixelsPerInch := Graphic.XDotsPerInch;
    Graphic.Canvas.Font.Size := Round(32*(Graphic.XDotsPerInch/300)*Zoompercent/100);
    {Pre := 16;
    for i in [36,32,28,26,24,22,20,18,16,14,12,10,8,6] do
    begin
      Graphic.Canvas.Font.Size := i;
      Text_W := Graphic.Canvas.TextWidth(Constr);
      //Showmessage(inttostr(DGraphic.Canvas.Font.Size)+#13+inttostr(Text_W)+#13+inttostr(DGraphic.Width div 2));
      if Text_W > ((Graphic.Width div 3) * 2) then
      begin
        Graphic.Canvas.Font.Size := Pre;
        Break;
      end;
      Pre := i;
    end;}
    //Showmessage(inttostr(Graphic.Canvas.Font.Size));
    Text_W := Graphic.Canvas.TextWidth(Constr)+Round(1.5/2.54*Graphic.XDotsPerInch*Zoompercent/100);
    //Showmessage(inttostr(Graphic.Canvas.Font.Size)+#13+inttostr(Graphic.Width)+#13+inttostr(Text_W));
    While Text_W > Graphic.Width do
    begin
      Graphic.Canvas.Font.Size := Graphic.Canvas.Font.Size-1;
      Text_W := Graphic.Canvas.TextWidth(Constr)+Round(1.5/2.54*Graphic.XDotsPerInch*Zoompercent/100);
      //Showmessage(inttostr(Graphic.Canvas.Font.Size)+#13+inttostr(Graphic.Width)+#13+inttostr(Text_W));
    end;
    //Showmessage(inttostr(Graphic.Canvas.Font.Size));
    Text_H := Graphic.Canvas.TextHeight(Constr);
    Water_SP := Round(0.4/2.54*Graphic.XDotsPerInch*Zoompercent/100);
    WaterW := Text_W;
    WaterH := Text_H*2+Water_SP;// div 6;
    //Showmessage(inttostr(Text_W)+#13+inttostr(Text_H)+#13+inttostr(WaterH));
    WatermarkImage1.NewImage(WaterW,WaterH,ifTrueColor, nil, Graphic.XDotsPerInch,Graphic.YDotsPerInch);
    WatermarkImage1.Canvas.Brush.Color := clWhite;
    WatermarkImage1.Canvas.FillRect(Rect(0,0,WatermarkImage1.width,WatermarkImage1.Height));
    WatermarkImage1.Canvas.Font.PixelsPerInch := Graphic.XDotsPerInch;
    WatermarkImage1.Canvas.Font.Size := Graphic.Canvas.Font.Size;
    WatermarkImage1.Canvas.Font.Style := [fsBold];
    WatermarkImage1.Canvas.Font.Name := '標楷體';
    WatermarkImage1.Canvas.TextOut(1,1,Content);
    WatermarkImage1.Canvas.TextOut(1,Text_H div 2+Water_SP,content1);
    //Showmessage(inttostr(WatermarkImage1.Width)+#13+inttostr(WatermarkImage1.Height));
    Rotate(WatermarkImage1,-30);  //轉330度
    //Showmessage(inttostr(WatermarkImage1.Width)+#13+inttostr(WatermarkImage1.Height));
    WatermarkImage2.NewImage(WaterW,WaterH,ifTrueColor, nil, 0, 0);
    WatermarkImage2.Canvas.Brush.Color := clWhite;
    WatermarkImage2.Canvas.FillRect(Rect(0,0,WatermarkImage2.width,WatermarkImage2.Height));
    WatermarkImage2.Canvas.Font.PixelsPerInch := Graphic.XDotsPerInch;
    WatermarkImage2.Canvas.Font.Size := Graphic.Canvas.Font.Size;
    WatermarkImage2.Canvas.Font.Style := [fsBold];
    WatermarkImage2.Canvas.Font.Name := '標楷體';
    WatermarkImage2.Canvas.TextOut(1,1,Content);
    WatermarkImage2.Canvas.TextOut(1,Text_H div 2+Water_SP,content1);
    //WatermarkImage2.Canvas.TextOut(1,WaterH div 2,Content);
    //WatermarkImage2.Canvas.TextOut(1,WaterH div 12,content1);
    Rotate(WatermarkImage2,40);  //轉330度
    WatermarkImage.NewImage(Graphic.Width,Graphic.Height,ifTrueColor, nil, 0, 0);
    WatermarkImage.Canvas.Brush.Color := clWhite;
    WatermarkImage.Canvas.FillRect(Rect(0,0,WatermarkImage.width,WatermarkImage.Height));
    // Create the watermark transform
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage1.Width;
    sourrect.Bottom := WatermarkImage1.Height;
    //showmessage(inttostr(Zoompercent)+#13+inttostr(Round(1/2.54*Graphic.XDotsPerInch*Zoompercent/100)));
    destrect.Left := Round(3/2.54*Graphic.XDotsPerInch*Zoompercent/100);
    destrect.Top :=  (WatermarkImage.Height Div 4)-(WatermarkImage1.Height div 2);
    destrect.Right := destrect.Left+WatermarkImage1.Width;
    destrect.Bottom := destrect.Top + WatermarkImage1.Height;
    //Showmessage(inttostr(WatermarkImage.Height)+#13+inttostr(destrect.Top)+#13+inttostr(destrect.Bottom)+#13+inttostr(WatermarkImage1.Height));
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage2.Width;
    sourrect.Bottom := WatermarkImage2.Height;
    destrect.Left := Round(3/2.54*Graphic.XDotsPerInch*Zoompercent/100);
    destrect.Top := ((WatermarkImage.Height div 4) * 3) -(WatermarkImage2.Height div 2);
    destrect.Right := destrect.Left+WatermarkImage2.Width;
    destrect.Bottom := destrect.Top+WatermarkImage2.Height;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage2.Canvas,sourrect);
    {destrect.Left := WatermarkImage.Width-WatermarkImage1.Width;
    destrect.Top := WatermarkImage.Height-WatermarkImage1.Height;
    destrect.Right := WatermarkImage.Width;
    destrect.Bottom := WatermarkImage.Height;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
     }
    WatermarkTransform:=TWatermarkTransform.Create;
    try
      // Set the watermark to use
      WatermarkTransform.Watermark:=WatermarkImage;
      // Select the position where place the watermark: centered !
      WatermarkTransform.Position:=Point(0,0);
      //WatermarkTransform.Position:=Point((ImageScrollBox.DisplayedDGraphic.Width-WatermarkImage.Width) div 2,
        //                                 (ImageScrollBox.DisplayedDGraphic.Height-WatermarkImage.Height) div 2);
      // Set the opacity %
      WatermarkTransform.Opacity:=Opacity;
      // Apply the watermark
      WatermarkTransform.Apply(Graphic);
      //ImageScrollBox.Redraw(False);
      //ShowMessage('Watermark applied at image center with 25% opacity !');
    finally
     WatermarkTransform.Free;
    end;
  finally
    WatermarkImage.Free;
    WatermarkImage1.Free;
    WatermarkImage2.Free;
   end;
end;
procedure Watermark1_Hong_New(Opacity:Byte;Content,Content1:String;Graphic,DisGraphic:TDibGraphic;ImgH:Integer); //自行產生文字330度印在左上右下
Var WatermarkTransform:TWatermarkTransform;
    WatermarkImage:TDibGraphic;
    WatermarkImage1:TDibGraphic;
    WatermarkImage2:TDibGraphic;
    destrect,sourRect : Trect;
    WaterH,WaterW : Integer;
    Text_H,Text_W : Integer;
    i,pre : Integer;
    Zoompercent : Single;
    Water_SP : Integer;
    Constr : String;
begin
   // Allows user to select a file (the watermark)
   // Create the graphics specific for the selected extension
   if Content = '' then Exit;
   Constr := Content;
   if length(Content1) > length(Content) then
     Constr := Content1;
   //Showmessage(inttostr(ImgH)+#13+inttostr(Graphic.Height)+#13+inttostr(Graphic.XDotsPerInch));
   Zoompercent := DisGraphic.Height/ImgH *100;
   //Showmessage(floattostr(Zoompercent));
   WatermarkImage1:=TDibGraphic.Create;
   WatermarkImage2:=TDibGraphic.Create;
   WatermarkImage:=TDibGraphic.Create;
   try
    // Load the watermark
    //WatermarkImage1.Assign(logoBmp);
    //showmessage(inttostr(Graphic.Width)+#13+inttostr(Graphic.Height));
    Graphic.Canvas.Font.Style := [fsBold];
    Graphic.Canvas.Font.Name := '標楷體';
    Graphic.Canvas.Font.PixelsPerInch := Graphic.XDotsPerInch;
    Graphic.Canvas.Font.Size := 16;
    {Pre := 16;
    for i in [36,32,28,26,24,22,20,18,16,14,12,10,8,6] do
    begin
      Graphic.Canvas.Font.Size := i;
      Text_W := Graphic.Canvas.TextWidth(Constr);
      //Showmessage(inttostr(DGraphic.Canvas.Font.Size)+#13+inttostr(Text_W)+#13+inttostr(DGraphic.Width div 2));
      if Text_W > ((Graphic.Width div 3) * 2) then
      begin
        Graphic.Canvas.Font.Size := Pre;
        Break;
      end;
      Pre := i;
    end;}
    //Showmessage(inttostr(DGraphic.Canvas.Font.Size));
    Text_W := Graphic.Canvas.TextWidth(Constr)+Round(1.5/2.54*Graphic.XDotsPerInch);
    //Showmessage(inttostr(Graphic.Canvas.Font.Size)+#13+inttostr(Graphic.Width)+#13+inttostr(Text_W));
    Text_H := Graphic.Canvas.TextHeight(Constr);
    Water_SP := Round(0.4/2.54*Graphic.XDotsPerInch*Zoompercent/100);
    WaterW := Text_W;
    WaterH := Text_H*2+Water_SP;// div 6;
    //Showmessage(inttostr(Text_W)+#13+inttostr(Text_H)+#13+inttostr(WaterH));
    WatermarkImage1.NewImage(WaterW,WaterH,ifTrueColor, nil, Graphic.XDotsPerInch,Graphic.YDotsPerInch);
    WatermarkImage1.Canvas.Brush.Color := clWhite;
    WatermarkImage1.Canvas.FillRect(Rect(0,0,WatermarkImage1.width,WatermarkImage1.Height));
    WatermarkImage1.Canvas.Font.PixelsPerInch := Graphic.XDotsPerInch;
    WatermarkImage1.Canvas.Font.Size := Graphic.Canvas.Font.Size;
    WatermarkImage1.Canvas.Font.Style := [fsBold];
    WatermarkImage1.Canvas.Font.Name := '標楷體';
    WatermarkImage1.Canvas.TextOut(1,1,Content);
    WatermarkImage1.Canvas.TextOut(1,Text_H div 2+Water_SP,content1);
    //Showmessage(inttostr(WatermarkImage1.Width)+#13+inttostr(WatermarkImage1.Height));
    Rotate(WatermarkImage1,-30);  //轉330度
    //Showmessage(inttostr(WatermarkImage1.Width)+#13+inttostr(WatermarkImage1.Height));
    WatermarkImage2.NewImage(WaterW,WaterH,ifTrueColor, nil, 0, 0);
    WatermarkImage2.Canvas.Brush.Color := clWhite;
    WatermarkImage2.Canvas.FillRect(Rect(0,0,WatermarkImage2.width,WatermarkImage2.Height));
    WatermarkImage2.Canvas.Font.PixelsPerInch := Graphic.XDotsPerInch;
    WatermarkImage2.Canvas.Font.Size := Graphic.Canvas.Font.Size;
    WatermarkImage2.Canvas.Font.Style := [fsBold];
    WatermarkImage2.Canvas.Font.Name := '標楷體';
    WatermarkImage2.Canvas.TextOut(1,1,Content);
    WatermarkImage2.Canvas.TextOut(1,Text_H div 2+Water_SP,content1);
    //WatermarkImage2.Canvas.TextOut(1,WaterH div 2,Content);
    //WatermarkImage2.Canvas.TextOut(1,WaterH div 12,content1);
    Rotate(WatermarkImage2,-150);  //轉330度
    WatermarkImage.NewImage(DisGraphic.Width,DisGraphic.Height,ifTrueColor, nil, 0, 0);
    WatermarkImage.Canvas.Brush.Color := clWhite;
    WatermarkImage.Canvas.FillRect(Rect(0,0,WatermarkImage.width,WatermarkImage.Height));
    // Create the watermark transform
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage1.Width;
    sourrect.Bottom := WatermarkImage1.Height;
    //showmessage(inttostr(Zoompercent)+#13+inttostr(Round(1/2.54*Graphic.XDotsPerInch*Zoompercent/100)));
    destrect.Left := Round(1/2.54*Graphic.XDotsPerInch*Zoompercent/100);
    destrect.Top :=  (WatermarkImage.Height Div 4)-(Round(WatermarkImage1.Height*Zoompercent/100) div 2);
    destrect.Right := destrect.Left+Round(WatermarkImage1.Width*Zoompercent/100);
    destrect.Bottom := destrect.Top + Round(WatermarkImage1.Height*Zoompercent/100);
    //Showmessage(inttostr(WatermarkImage.Height)+#13+inttostr(destrect.Top)+#13+inttostr(destrect.Bottom)+#13+inttostr(WatermarkImage1.Height));
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage2.Width;
    sourrect.Bottom := WatermarkImage2.Height;
    destrect.Left := Round(1/2.54*Graphic.XDotsPerInch*Zoompercent/100);
    destrect.Top := ((WatermarkImage.Height div 4) * 3) -(Round(WatermarkImage2.Height*Zoompercent/100) div 2);
    destrect.Right := destrect.Left+Round(WatermarkImage2.Width*Zoompercent/100);
    destrect.Bottom := destrect.Top+Round(WatermarkImage2.Height*Zoompercent/100);
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage2.Canvas,sourrect);
    {destrect.Left := WatermarkImage.Width-WatermarkImage1.Width;
    destrect.Top := WatermarkImage.Height-WatermarkImage1.Height;
    destrect.Right := WatermarkImage.Width;
    destrect.Bottom := WatermarkImage.Height;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
     }
    WatermarkTransform:=TWatermarkTransform.Create;
    try
      // Set the watermark to use
      WatermarkTransform.Watermark:=WatermarkImage;
      // Select the position where place the watermark: centered !
      WatermarkTransform.Position:=Point(0,0);
      //WatermarkTransform.Position:=Point((ImageScrollBox.DisplayedDGraphic.Width-WatermarkImage.Width) div 2,
        //                                 (ImageScrollBox.DisplayedDGraphic.Height-WatermarkImage.Height) div 2);
      // Set the opacity %
      WatermarkTransform.Opacity:=Opacity;
      // Apply the watermark
      WatermarkTransform.Apply(DisGraphic);
      //ImageScrollBox.Redraw(False);
      //ShowMessage('Watermark applied at image center with 25% opacity !');
    finally
     WatermarkTransform.Free;
    end;
  finally
    WatermarkImage.Free;
    WatermarkImage1.Free;
    WatermarkImage2.Free;
   end;
end;
procedure Watermark2(logoBmp:TBitmap;Opacity:Byte;Content:String;Graphic:TDibGraphic);
Var WatermarkTransform:TWatermarkTransform;
    WatermarkImage:TDibGraphic;
    WatermarkImage1:TDibGraphic;
    S : String;
    PW,PH : Integer;
    x,y : Integer;
    WH,WW : Integer;
    destrect,sourRect : Trect;
    WaterH,WaterW : Integer;
begin
   // Allows user to select a file (the watermark)
   // Create the graphics specific for the selected extension
   WatermarkImage1:=TDibGraphic.Create;
   WatermarkImage:=TDibGraphic.Create;
   try
    // Load the watermark
    WatermarkImage1.Assign(logoBmp);
    IF (Graphic.Height > Graphic.Width) Then
    begin
      WaterW := Round((1.2 / 2.54) * Graphic.XDotsPerInch);
      WaterH := Round((5 / 2.54) * Graphic.XDotsPerInch);
      //WaterW := (Graphic.Width div 18);
      //WaterH := Round( WatermarkImage1.Height * (WaterW / WatermarkImage1.Width));
    end
    Else
    begin
      WaterW := Round((1.2 / 2.54) * Graphic.XDotsPerInch);
      WaterH := Round((5 / 2.54) * Graphic.XDotsPerInch);
      //WaterH := (Graphic.Height div 18);
      //WaterW := Round( WatermarkImage1.Width * (WaterH / WatermarkImage1.Height));
    end;
    WatermarkImage.NewImage(WaterW,WaterH+30,ifTrueColor, nil, 0, 0);
    WatermarkImage.Canvas.Brush.Color := clWhite;
    WatermarkImage.Canvas.FillRect(Rect(0,0,WatermarkImage.width,WatermarkImage.Height));
    // Create the watermark transform
    sourrect.Left := 0;
    sourrect.Top := 0;
    sourrect.Right := WatermarkImage1.Width;
    sourrect.Bottom := WatermarkImage1.Height;
    destrect.Left := 0;
    destrect.Top := 0;
    destrect.Right := WaterW;
    destrect.Bottom := WaterH;
    WatermarkImage.Canvas.CopyRect(destrect,WatermarkImage1.Canvas,sourrect);
    WH := Graphic.Height div 2;
    WW := Graphic.Width div 2;
    WatermarkTransform:=TWatermarkTransform.Create;
    try
      // Set the watermark to use
      WatermarkTransform.Watermark:=WatermarkImage;
      // Select the position where place the watermark: centered !
      {WatermarkTransform.Position:=Point(WW -(WatermarkImage.Width div 2),
                                        WH -(WatermarkImage.Height div 2));}
      WatermarkTransform.Position:=Point(20,20);
      //WatermarkTransform.Position:=Point((ImageScrollBox.DisplayedGraphic.Width-WatermarkImage.Width) div 2,
        //                                 (ImageScrollBox.DisplayedGraphic.Height-WatermarkImage.Height) div 2);
      IF Content <> '' Then
      begin
        WatermarkTransform.Watermark.Canvas.Font.Style := [fsBold];
        WatermarkTransform.Watermark.Canvas.Font.Size := 36;
        S := Content;
        PW := WatermarkTransform.Watermark.Canvas.TextWidth(Content);
        PH := WatermarkTransform.Watermark.Canvas.TextHeight(Content);
        WatermarkTransform.Watermark.Canvas.TextOut((WatermarkTransform.Watermark.Width - PW) Div 2,WatermarkTransform.Watermark.Height-PH-10,Content);
      end;
      // Set the opacity %
      WatermarkTransform.Opacity:=Opacity;
      // Apply the watermark
      WatermarkTransform.Apply(Graphic);
      //ImageScrollBox.Redraw(False);
      //ShowMessage('Watermark applied at image center with 25% opacity !');
    finally
     WatermarkTransform.Free;
    end;
  finally
    WatermarkImage.Free;
    WatermarkImage1.Free;
   end;
end;
Procedure FindPoint(Graphic:TDibGraphic;Var UpPointL,UpPointR,DownPointL:TPoint;Var FormWidth,FormHeight:Integer;Anchor:String);
var
  XDpi,YDpi : Integer;
  XLen,XLen1,YLen : Integer;
  LSeg,RSeg : Integer;
  compsize : Integer;
begin
  XDpi := Graphic.XDotsPerInch;
  YDpi := Graphic.YDotsPerInch;
  XLen := Round(1 * XDpi);
  XLen1 := Round(4 * XDpi);
  YLen := Round(1 * XDpi);
  LSeg := 1;//Round(0.1 / 2.54 * XDpi);
  RSeg := 1;
  compsize := Round(0.15/2.54 * XDpi);  //要找定位的長度
  if Anchor <> 'NONE' then
  begin
    UpPointL := Checked_Start(Graphic,LSeg,LSeg,XLen,YLen ,compsize,1,Anchor);
    IF ((UpPointL.X=0) and (UpPointL.Y=0)) Then
      UpPointL := Checked_Start(Graphic,LSeg,LSeg,XLen1,YLen ,compsize,1,Anchor);
    DownPointL := Checked_Start(Graphic,Lseg,Graphic.Height- Lseg,XLen,Graphic.Height-YLen ,compsize,2,Anchor); //ToY必須>=compsize
    IF ((DownPointL.X=0) and (DownPointL.Y=Graphic.Height)) Then
    begin
      DownPointL := Checked_Start(Graphic,Lseg,Graphic.Height- Lseg,XLen1,Graphic.Height-YLen ,compsize,2,Anchor);
    end;
    UpPointR := Checked_Start(Graphic,Graphic.Width - Rseg,Rseg,Graphic.Width-XLen,YLen ,compsize,3,Anchor); //FromX必須>=compsize
    FormWidth := UpPointR.X - UpPointL.X;
    FormHeight := DownPointL.Y - UpPointL.Y;
  end
  Else
  begin
    UpPointL.X := 0;
    UpPointL.Y := 0;
    FormWidth := Graphic.Width;
    FormHeight := Graphic.Height;
  end;
  {IF ((UpPointR.X=ImageScrollBox1.Graphic.Width)and(UpPointR.Y=0)) Then
    UpPointR := Checked_Start(ISB_BW.Graphic,ImageScrollBox1.Graphic.Width - Rseg,Rseg,ImageScrollBox1.Graphic.Width-XLen1,YLen ,compsize,3,AnchorMode); //FromX必須>=compsize
  }
  //DownPointR := Checked_Start(ISB_BW.Graphic,ImageScrollBox1.Graphic.Width -Rseg,ImageScrollBox1.Graphic.Height- Rseg,ImageScrollBox1.Graphic.Width-XLen ,ImageScrollBox1.Graphic.Height-YLen ,compsize,4,AnchorMode); //FromX必須>=compsize
end;
Procedure FindPoint(Graphic:TDibGraphic;Var UpPointL,UpPointR,DownPointL:TPoint;Anchor:String);overload;
var
  XDpi,YDpi : Integer;
  XLen,XLen1,YLen : Integer;
  LSeg,RSeg : Integer;
  compsize : Integer;
begin
  XDpi := Graphic.XDotsPerInch;
  YDpi := Graphic.YDotsPerInch;
  XLen := Round(1 * XDpi);
  XLen1 := Round(4 * XDpi);
  YLen := Round(1 * XDpi);
  LSeg := 1;//Round(0.1 / 2.54 * XDpi);
  RSeg := 1;
  compsize := Round(0.15/2.54 * XDpi);  //要找定位的長度
  UpPointL := Checked_Start(Graphic,LSeg,LSeg,XLen,YLen ,compsize,1,Anchor);
  IF ((UpPointL.X=0) and (UpPointL.Y=0)) Then
    UpPointL := Checked_Start(Graphic,LSeg,LSeg,XLen1,YLen ,compsize,1,Anchor);
  DownPointL := Checked_Start(Graphic,Lseg,Graphic.Height- Lseg,XLen,Graphic.Height-YLen ,compsize,2,Anchor); //ToY必須>=compsize
  IF ((DownPointL.X=0) and (DownPointL.Y=Graphic.Height)) Then
  begin
    DownPointL := Checked_Start(Graphic,Lseg,Graphic.Height- Lseg,XLen1,Graphic.Height-YLen ,compsize,2,Anchor);
  end;
  UpPointR := Checked_Start(Graphic,Graphic.Width - Rseg,Rseg,Graphic.Width-XLen,YLen ,compsize,3,Anchor); //FromX必須>=compsize
end;
function GetBlackSpots(Graphic:TDibGraphic;L, T, R, B, Ratio: Integer): WideString;
var
  S : TStringlist;
  i : Integer;
begin
  Result := '';
  IF Graphic.ImageFormat = ifBlackWhite Then
  begin
    S := TStringlist.Create;
    try
      DorwFindRect(Graphic,L,T,R,B,Ratio);
      for i := 1 to BlackSpotsCount do
      begin
        S.Add(Format('%d,%d,%d,%d',[BlackSpots[i].Left,BlackSpots[i].Top,BlackSpots[i].Right,BlackSpots[i].Bottom ]));
      end;
    Result := S.Text;
    Finally
    S.Free;
    end;
  end;
end;
Procedure FindBlackPoint(Graphic:TDibGraphic;Var BlackPoint:Tpoint);
var
  Seg : Integer;
  W,H : Integer;
  BlackSpotList,SiteList : TStringlist;
  i : Integer;
  P : TPoint;
begin
  BlackPoint.X := 0;
  BlackPoint.Y := 0;
  IF Graphic.ImageFormat = ifBlackWhite Then
  begin
    BlackSpotList := TStringlist.Create;
    SiteList := TStringlist.Create;
    try
      Seg := Round(1/2.54*Graphic.XDotsPerInch);  //1公分的邊
      W := Graphic.Width;
      H := Graphic.Height;
      BlackSpotList.Text := GetBlackSpots(Graphic,W-Seg,0,W,H,80);
      If BlackSpotList.Count > 0 Then
      begin
        BlackPoint := Str2Point(BlackSpotList.Strings[0]);
      end;
      For i:=1 to BlackSpotList.Count -1 do
      begin
        P := Str2Point(BlackSpotList.Strings[i]);
        If (p.X+p.Y) > (BlackPoint.X +BlackPoint.Y) Then
        begin
          BlackPoint := p;
        end;
      end;
    finally
      BlackSpotList.Free;
      SiteList.Free;
    end;
  end;
end;
Function Get_OMR(Graphic:TDibGraphic;iRect:Trect):integer; //取點數
var i,n,x,y:integer;
begin
  i:=0;
  n:=0;
  for y := iRect.Top to iRect.Bottom -1 do
    for x := iRect.Left to iRect.Right -1 do
    begin
      if Graphic.Canvas.Pixels[x,y] = clBlack Then
         inc(n);
    end;
  result :=n;
end;
Function GetSelectRect(ISB:TImageScrollBox):TRect; //取出影像上的框選範圍
var
  Rt: TRect;
  XDpi,YDpi : single;
  ct : Integer;
begin
  Result := Rect(0,0,0,0);
  XDpi:=ISB.Graphic.XDotsPerInch;
  YDpi:=ISB.Graphic.YDotsPerInch;
  if ISB.MouseHandler is TRubberBandMouseHandler then
  begin
    TRubberBandMouseHandler(ISB.MouseHandler).GetSourceSelection(Rt);
    Result := Rt;
  end;
end;
function GetSelectRect2String(ISB:TImageScrollBox;UpPointL:TPoint): WideString;
var
  rt2: trect;
  XDpi,YDpi : single;
  ct : Integer;
  L,T,R,B : String;
begin
  //IF (X1 = X2) and  (Y1 = Y2) Then Exit;
  XDpi:=ISB.Graphic.XDotsPerInch;
  YDpi:=ISB.Graphic.YDotsPerInch;
  if ISB.MouseHandler is TRubberBandMouseHandler then
  begin
    TRubberBandMouseHandler(ISB.MouseHandler).GetSourceSelection(rt2);
    L := Format('%3.2f',[(rt2.Left - UpPointL.X) / Xdpi * 2.54 ]);
    T := Format('%3.2f',[(rt2.Top - UpPointL.Y) / Xdpi * 2.54]);
    R := Format('%3.2f',[(rt2.right - UpPointL.X) / Xdpi * 2.54]);
    B := Format('%3.2f',[(rt2.bottom - UpPointL.Y)/ Xdpi * 2.54]);
    Result := L+#13+T+#13+R+#13+B;
  end;
end;
Procedure SetSelectRect(ISB:TImageScrollBox;iRect:TRect); //顯示出指定的框
var
  Rt : TRect;
  XDpi,YDpi : single;
  XZoom       : Single;
  YZoom       : Single;
begin
  XDpi:=ISB.Graphic.XDotsPerInch;
  YDpi:=ISB.Graphic.YDotsPerInch;
  rt.Left :=Round(iRect.Left*ISB.ZoomPercent/100);
  rt.Top :=Round(iRect.Top*ISB.ZoomPercent/100);
  rt.Right :=Round(iRect.Right*ISB.ZoomPercent/100);
  rt.Bottom :=Round(iRect.Bottom*ISB.ZoomPercent/100);
  //20200217 加這段讓運算後可能不到1的變1,才能順利畫出框
  if rt.Left = 0 then Rt.Left := 1;
  if rt.Top = 0 then Rt.Top := 1;
  if rt.Right = isb.DisplayedGraphic.Width then
    rt.Right := isb.DisplayedGraphic.Width-1;
  if rt.Bottom = isb.DisplayedGraphic.Height then
    rt.Bottom := isb.DisplayedGraphic.Height-1;
  //20200217 加這段讓運算後可能不到1的變1,才能順利畫出框
  if ISB.MouseHandler is TRubberBandMouseHandler then
  begin
    TRubberBandMouseHandler(ISB.MouseHandler).setselectrect(rt);
  end;
  ISB.HorzScrollBar.Position := rt.Left;
  ISB.VertScrollBar.Position := rt.Top-20;
end;
Procedure SetSelectRect_Original(ISB:TImageScrollBox;iRect:TRect); //顯示出指定的框(不縮放)
var
  Rt : TRect;
  XDpi,YDpi : single;
  XZoom       : Single;
  YZoom       : Single;
begin
  XDpi:=ISB.Graphic.XDotsPerInch;
  YDpi:=ISB.Graphic.YDotsPerInch;
  rt.Left :=Round(iRect.Left);
  rt.Top :=Round(iRect.Top);
  rt.Right :=Round(iRect.Right);
  rt.Bottom :=Round(iRect.Bottom);
  if ISB.MouseHandler is TRubberBandMouseHandler then
  begin
    TRubberBandMouseHandler(ISB.MouseHandler).setselectrect(rt);
  end;
  ISB.HorzScrollBar.Position := rt.Left;
  ISB.VertScrollBar.Position := rt.Top-20;
end;
Function GetSelectRect_Back(ISB:TImageScrollBox):TRect; //取出影像上的框選範圍(從右下回來)
var
  rt2: trect;
  ct : Integer;
  L1,T1,R1,B1 : String;
begin
  //IF (X1 = X2) and  (Y1 = Y2) Then Exit;
  Result := Rect(0,0,0,0);
  if ISB.MouseHandler is TRubberBandMouseHandler then
  begin
    TRubberBandMouseHandler(ISB.MouseHandler).GetSourceSelection(rt2);
    rt2.Left := ISB.Graphic.Width - rt2.Left;
    rt2.Top := ISB.Graphic.Height - rt2.Top;
    rt2.Right := ISB.Graphic.Width - rt2.Right;
    rt2.Bottom := ISB.Graphic.Height - rt2.Bottom;
    Result := rt2;
  end;
end;
function GetSelectRect_Black2String(ISB:TImageScrollBox;BlackPoint:TPoint): WideString;
var
  rt2: trect;
  XDpi,YDpi : single;
  ct : Integer;
  L1,T1,R1,B1 : String;
begin
  //IF (X1 = X2) and  (Y1 = Y2) Then Exit;
  Result := '';
  XDpi:=ISB.Graphic.XDotsPerInch;
  YDpi:=ISB.Graphic.YDotsPerInch;
  if ISB.MouseHandler is TRubberBandMouseHandler then
  begin
    TRubberBandMouseHandler(ISB.MouseHandler).GetSourceSelection(rt2);
    IF (BlackPoint.X > 0) and (BlackPoint.Y > 0) Then
    begin
      L1 := Format('%3.2f',[(BlackPoint.X - rt2.Left) / Xdpi * 2.54 ]);
      T1 := Format('%3.2f',[(BlackPoint.Y - rt2.Top) / Xdpi * 2.54]);
      R1 := Format('%3.2f',[(BlackPoint.X - rt2.right) / Xdpi * 2.54]);
      B1 := Format('%3.2f',[(BlackPoint.Y - rt2.bottom)/ Xdpi * 2.54]);
      Result := L1+#13+T1+#13+R1+#13+B1;
    end;
  end;
end;
Procedure ShowKeyinRect(ISB:TImageScrollBox;iRect:TRect); //顯示登打位置
var
  XZoom       : Single;
  YZoom       : Single;
  newZoom     : Single;
  x1,x2,y1,y2 : Integer;
  theRect : Trect;
  Bmp : TBitmap;
  FAnnotationCanvas : TCanvas;
  i : Integer;
begin
  Therect:=  iRect;
  XZoom   :=  ( (ISB.Width-20)/(Therect.Right - Therect.Left + 1) ) * 100;
  YZoom   :=  ( (ISB.Height-20)/(Therect.Bottom - Therect.Top + 1+40)) * 100;
  { limit the zoom value to 1000 % }
  newZoom := MinFloat(MinFloat(XZoom,100), YZoom) ;
  //NewZoom := ISB.ZoomPercent;
  ISB.ZoomPercent := newZoom ;
  ISB.HorzScrollBar.Position := SafeTrunc(Therect.Left* newZoom /100 );
  ISB.VertScrollBar.Position := SafeTrunc((Therect.Top-20) * newZoom /100 );
  //加入區域底色
  x1 := SafeTrunc(Therect.Left* newZoom /100 )-ISB.HorzScrollBar.Position;
  x2 := SafeTrunc(Therect.Right* newZoom /100 )-ISB.HorzScrollBar.Position;
  y1 := SafeTrunc(Therect.Top* newZoom /100 )-ISB.VertScrollBar.Position;
  y2 := SafeTrunc(Therect.bottom* newZoom /100 )-ISB.VertScrollBar.Position;
  FAnnotationCanvas := TCanvas.Create;
  FAnnotationCanvas.Handle := GetDC( ISB.Handle );
  FAnnotationCanvas.Pen.Style := psSolid;
  FAnnotationCanvas.Pen.Color := $00FEFAAD;  //&H80000005//65535;
  FAnnotationCanvas.Pen.Width := 1;
  FAnnotationCanvas.Pen.Mode := pmMask;
  { Draw box on screen }
  for i := y1 to y2 do begin
    FAnnotationCanvas.MoveTo( x1, i );
    FAnnotationCanvas.LineTo( x2, i );
  end;
  ReleaseDC(0,FAnnotationCanvas.Handle);
  FAnnotationCanvas.Free;
end;
procedure ImageResize(Graphic:TDibGraphic;DesWidth,DesHeight:Integer);
var
  Transform    : TResizeTransform;
  FUndoGraphic  : TDibGraphic;
begin
  FUndoGraphic  := TDibGraphic.Create;
  Transform := TResizeTransform.Create;
  try
    Transform.Width      := DesWidth;
    Transform.Height     := DesHeight;
    //Transform.Interpolated := True;
    //Transform.Filter := ifLanczos3;
    FUndoGraphic.Assign(Graphic);
    Transform.ApplyOnDest(FUndoGraphic,Graphic);
  finally
    Transform.Free;
    FUndoGraphic.Free;
  end;
end;
Procedure DpiResize(Graphic:TDibGraphic;DesDpi:Integer;CheckDpi:Boolean); //Dpi不足時放大Size
Var
  NewWidth : Integer;
  NewHeight : Integer;
begin
  if CheckDpi then   //CUB 出現Dpi是1 要用這個才不會出現out of resource
  begin
    if (Graphic.XDotsPerInch > DesDpi) and (Graphic.YDotsPerInch> DesDpi) then
    begin
      NewWidth := Round(Graphic.Width * (DesDpi / Graphic.XDotsPerInch));
      NewHeight := Round(Graphic.Height * (DesDpi / Graphic.YDotsPerInch));
      Graphic.XDotsPerInch := DesDpi;
      Graphic.YDotsPerInch := DesDpi;
      ImageResize(Graphic,NewWidth,NewHeight);
    end;
  end
  Else   //永豐傳真一定要用到這一段
  begin
    NewWidth := Round(Graphic.Width * (DesDpi / Graphic.XDotsPerInch));
    NewHeight := Round(Graphic.Height * (DesDpi / Graphic.YDotsPerInch));
    Graphic.XDotsPerInch := DesDpi;
    Graphic.YDotsPerInch := DesDpi;
    ImageResize(Graphic,NewWidth,NewHeight);
  end;
end;
Procedure DrawPointLine(ISB:TImageScrollBox;UpPointL,UpPointR,DownPointL:TPoint); //畫十字位置
begin
  ISB.DisplayedGraphic.Canvas.pen.Color := clGreen;
  ISB.DisplayedGraphic.Canvas.Font.Size := 16;
  //左上
  ISB.DisplayedGraphic.Canvas.MoveTo(0,0);
  ISB.DisplayedGraphic.Canvas.LineTo(Round(uppointL.X*ISB.ZoomPercent/100),Round(uppointL.y*ISB.ZoomPercent/100));
  //左下
  ISB.DisplayedGraphic.Canvas.MoveTo(0,Round(ISB.Graphic.Height*ISB.ZoomPercent/100));
  ISB.DisplayedGraphic.Canvas.LineTo(Round(downpointL.X*ISB.ZoomPercent/100),Round(downpointL.y*ISB.ZoomPercent/100));
  //右上
  ISB.DisplayedGraphic.Canvas.MoveTo(Round(ISB.Graphic.Width*ISB.ZoomPercent/100),0);
  ISB.DisplayedGraphic.Canvas.LineTo(Round(uppointR.X*ISB.ZoomPercent/100),Round(uppointR.y*ISB.ZoomPercent/100));
  //ISB.DisplayedGraphic.Canvas.MoveTo(Round(ImageScrollBox1.Graphic.Width*ImageScrollBox1.ZoomPercent/100),Round(ImageScrollBox1.Graphic.Height*ImageScrollBox1.ZoomPercent/100));
  //ISB.DisplayedGraphic.Canvas.LineTo(Round(downpointR.X*ImageScrollBox1.ZoomPercent/100),Round(downpointR.y*ImageScrollBox1.ZoomPercent/100));
end;
Procedure Color2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer); //用RTS彩色轉黑白
var
  bmp:TBitmap;
  Xdpi,Ydpi : Integer;
begin
  XDpi := ISB.Graphic.XDotsPerInch;
  YDpi := ISB.Graphic.YDotsPerInch;
  bmp := TBitmap.Create;
  try
    bmp.Assign(ISB.Graphic);
    BmpGoVrs_Color(bmp,Para1,para2,para3);
    ISB.Graphic.Assign(bmp);
    ISB.Graphic.XDotsPerInch := XDpi;
    ISB.Graphic.YDotsPerInch := YDpi;
  Finally
  bmp.Free;
  end;
end;
Procedure Color2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer;Part:Trect); Overload; //用RTS彩色轉黑白
var
  bmp:TBitmap;
  Xdpi,Ydpi : Integer;
begin
  XDpi := ISB.Graphic.XDotsPerInch;
  YDpi := ISB.Graphic.YDotsPerInch;
  bmp := TBitmap.Create;
  try
    bmp.Assign(ISB.Graphic);
    BmpGoVrs_Color(bmp,Para1,para2,para3);
    ISB.Graphic.Assign(bmp);
    ISB.Graphic.XDotsPerInch := XDpi;
    ISB.Graphic.YDotsPerInch := YDpi;
  Finally
  bmp.Free;
  end;
end;
Procedure Gray2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer);
var
  bmp:TBitmap;
  Xdpi,Ydpi : Integer;
begin
  XDpi := ISB.Graphic.XDotsPerInch;
  YDpi := ISB.Graphic.YDotsPerInch;
  bmp := TBitmap.Create;
  try
    if ISB.Graphic.ImageFormat <> ifGray256 then
      Convertto256Gray(ISB.Graphic);
    bmp.Assign(ISB.Graphic);
    //bmp.PixelFormat := pf8bit;  //這樣接影像會拿到 24bit
    if bmp.PixelFormat = pf8bit then
      BmpGoVrs(bmp,Para1,para2,para3);
    if bmp.PixelFormat  = pf24bit then
      BmpGoVrs_Color(bmp,Para1,para2,para3);
    ISB.Graphic.Assign(bmp);
    ISB.Graphic.XDotsPerInch := XDpi;
    ISB.Graphic.YDotsPerInch := YDpi;
  Finally
  bmp.Free;
  end;
end;
Procedure Gray2BW_RTS(ISB:TImageScrollBox;Para1,Para2,Para3:Integer;Part:Trect); Overload; //用RTS灰階轉黑白
var
  bmp:TBitmap;
  Xdpi,Ydpi : Integer;
begin
  XDpi := ISB.Graphic.XDotsPerInch;
  YDpi := ISB.Graphic.YDotsPerInch;
  bmp := TBitmap.Create;
  try
    bmp.Assign(ISB.Graphic);
    bmp.PixelFormat := pf8bit;
    BmpGoVrs(bmp,Para1,para2,para3,Part);
    ISB.Graphic.Assign(bmp);
    ISB.Graphic.XDotsPerInch := XDpi;
    ISB.Graphic.YDotsPerInch := YDpi;
  Finally
  bmp.Free;
  end;
end;
procedure Emboss(ISB:TImageScrollBox);
var
    Transform : TEmbossTransform;
    FUndoGraphic : TDibGraphic;
begin
    FUndoGraphic := TDibGraphic.Create;
    FUndoGraphic.Assign(ISB.Graphic);
    Transform := TEmbossTransform.Create;
    try
        Transform.ApplyOnDest(FUndoGraphic, ISB.Graphic);
    finally
        Transform.Free;
        FUndoGraphic.Free;
    end;
    //ISB.Redraw(True);
end;
Procedure BrightnessImg(ISB:TImageScrollBox;Precent:Integer); //調整亮度
var
    Transform : TBrightnessTransform;
    FUndoGraphic : TDibGraphic;
begin
    FUndoGraphic := TDibGraphic.Create;
    FUndoGraphic.Assign(ISB.Graphic);
    Transform := TBrightnessTransform.Create;
    try
        Transform.Percent    := Precent;
        Transform.ApplyOnDest(FUndoGraphic, ISB.Graphic);
    finally
        Transform.Free;
        FUndoGraphic.Free;
    end;
    //ImageScrollBox.Redraw(True);
end;
Procedure ConvertToBW(Graphic : TDibGraphic); //轉成黑白
var
    Transform  : TImageFormatTransform;
    FUndoGraphic : TDibGraphic;
begin
  Transform := TImageFormatTransform.Create;
  FUndoGraphic := TDibGraphic.Create;
  try
    FUndoGraphic.Assign(Graphic);
    Transform.ImageFormat := ifBlackWhite;
    Transform.Quantize    := True;
    Transform.Dither      := False;
    Transform.ApplyOnDest(FUndoGraphic, Graphic);
  finally
    Transform.Free;
    FUndoGraphic.Free;
  end;
end;
Procedure ConvertTo256Gray(Graphic : TDibGraphic); //轉成256灰階
var
    Transform  : TImageFormatTransform;
    FUndoGraphic : TDibGraphic;
begin
  Transform := TImageFormatTransform.Create;
  FUndoGraphic := TDibGraphic.Create;
  try
    FUndoGraphic.Assign(Graphic);
    Transform.ImageFormat := ifGray256;
    Transform.Quantize    := True;
    Transform.Dither      := False;
    Transform.ApplyOnDest(FUndoGraphic, Graphic);
  finally
    Transform.Free;
    FUndoGraphic.Free;
  end;
end;
Procedure ClearLine(Graphic : TDibGraphic;bt:Integer); //清影像上的線條
Var
  GraphicA   : TTiffGraphic;
  i:integer;
begin
  GraphicA := TTiffGraphic.create;
  Try
    GraphicA.Assign(Graphic);
    For i:=8 to GraphicA.height-8 do
         MpsClearLineV(Graphic, GraphicA , i, 1,GraphicA.Width-1,bt );
     For i:=8 to GraphicA.Width-8 do
         MpsClearLineH(Graphic, GraphicA , i, 1,GraphicA.Height-1,bt );
    Graphic.Assign(GraphicA);
  Finally
  GraphicA.Free;
  end;
end;
procedure CropImg(Graphic : TDibGraphic;iRect:TRect);
var
  Transform : TCropTransform;
  FUndoGraphic : TDibGraphic;
begin
  FUndoGraphic := TDibGraphic.Create;
  FUndoGraphic.Assign(Graphic);
  Transform := TCropTransform.Create;
  Transform.CropMode :=  cmExtractRect;
  try
      Transform.Left   := iRect.Left;
      Transform.Right  := iRect.Right;
      Transform.Top    := iRect.Top;
      Transform.Bottom := iRect.Bottom;
      Transform.ApplyOnDest(FUndoGraphic, Graphic);
  finally
      Transform.Free;
      FUndoGraphic.Free;
  end;
end;
Function GetPosAngle(UpL,DownL,UpR:TPoint):Single; //取二直線夾角角度
var
  angle1,angle2 : Single;
  Wseg,Lseg : Integer;
begin
  Wseg := DownL.Y - UpL.Y;
  Lseg := UpL.X - DownL.X;
  Angle1 := (Arctan(Lseg/Wseg)*180/pi);
  Wseg := UpL.Y - UpR.Y;
  Lseg := UpR.X - UpL.X;
  Angle2 := (Arctan(Wseg/Lseg)*180/pi);
  Result := 90 +(Angle1)+(Angle2);
end;
function CheckSize(ISB:TImageScrollBox;UpL,UpR,DownL:TPoint;DefWidth,DefHeight:String): WideString;  //檢查Size並縮放
var
  NowW,NowH : Integer;
  DefW,DefH : Integer;
  SizeW,SizeH : Integer;
  ReSizeW,ReSizeH : Integer;
  XDpi,YDpi : Integer;
begin
  Result := '';
  XDpi := ISB.Graphic.XDotsPerInch;
  YDpi := ISB.Graphic.YDotsPerInch;
  DefW := Round(strtofloat(DefWidth)/2.54*XDpi);
  DefH := Round(strtofloat(DefHeight)/2.54*XDpi);
  SizeW := ISB.Graphic.Width;
  SizeH :=ISB.Graphic.Height;
  IF (DefW > 0) and (DefH > 0) and ((GetPosAngle(UpL,DownL,UpR) > 89) and (GetPosAngle(UpL,DownL,UpR) < 91)) Then
  begin
    NowW := UpR.X - UpL.X;
    NowH := DownL.Y - UpL.Y;
    If (NowW <> ISB.Graphic.Width) and (NowH <> ISB.Graphic.Height) and ((NowW <> DefW) or (NowH <> DefH)) then
    begin
      ReSizeW := Round(ISB.Graphic.Width * DefW / NowW);
      ReSizeH := Round(ISB.Graphic.Height * DefH /NowH);
      ImageResize(ISB.Graphic,ReSizeW,ReSizeH);
      ISB.Redraw(True);
      Result := '原長'+inttostr(SizeH)+',原寬'+inttostr(SizeW)+',長變動'+inttostr(ReSizeH)+',寬變動'+inttostr(ReSizeW);
    end;
  end;
end;
Function GetPixBW( srcGraphic : TDibGraphic; x,y:integer ):integer;
{******************************************************************************}
{ 取點的顏色  要用黑白的                }
{******************************************************************************}
var
  pSrcByte : ^Byte;
  srcBit : Byte;
begin
  result := 1;
  pSrcByte := Addr( srcGraphic.ScanLine[ y ] ^ [ x div 8 ] );
  srcBit := $80 shr ( x mod 8 );
  result := (pSrcByte^ and srcBit);
  if  result > 0 Then  result := 0
  else result := 1;
end;
Procedure BMPConverJpg(Source,SaveFileName:STring); //Bmp轉jpeg
var
  ConvertJpg :TTiffGraphic;
  BMP : TBitMap ;
begin
  ConvertJpg:=TTiffGraphic.Create;
  BMP :=TBitmap.Create;
  try
    BMP.LoadFromFile(source);
    ConvertJpg.Assign(BMP);
    ConvertJpg.Compression := tcNone;
    ConvertJpg.SaveToFile(SaveFileName);
  finally
  convertjpg.Free;
  Bmp.Free;
  end;
end;
Procedure BWTif2Jpg(Graphic:TDibGraphic); //黑白Tif轉彩色jpg
var
  JpgGraphic  : TJpegGraphic;
  FUndoGraphic       : TDibGraphic;
  Transform : TImageFormatTransform;
begin
  JpgGraphic := TJpegGraphic.Create;
  FUndoGraphic := TDibGraphic.Create;
  FUndoGraphic.Assign(Graphic);
  Transform := TImageFormatTransform.Create;
  try
    Transform.ImageFormat := ifTrueColor;
    Transform.ApplyOnDest(FUndoGraphic, Graphic);
  finally
    Transform.Free;
  end;
  FUndoGraphic.Free;
end;
Procedure Convert2Jpg(Graphic:TDibGraphic); //黑白Tif轉彩色jpg
var
  FUndoGraphic       : TDibGraphic;
  Transform : TImageFormatTransform;
begin
  FUndoGraphic := TDibGraphic.Create;
  FUndoGraphic.Assign(Graphic);
  Transform := TImageFormatTransform.Create;
  try
    Transform.ImageFormat := ifTrueColor;
    Transform.ApplyOnDest(FUndoGraphic, Graphic);
  finally
    Transform.Free;
  end;
  FUndoGraphic.Free;
end;
Procedure FieldMask(ISB:TImageScrollBox;SiteList,Mode: WideString;UpPointL:TPoint); //遮罩 Mode:mark mask
var
  i,Dpi : integer;
  Site_List,SiteStr : TStringlist;
  L,T,R,B :String;
  TB,LR : Integer;
  Ann : String;
  FixB : integer;
begin
  ISB.AlwaysShowAnnotations:= false;
  ISB.MouseMode := mmuser;
  ISB.MouseMode := mmselect;
  Dpi := ISB.Graphic.XDotsPerInch;
  FixB := Round(1/2.54*Dpi);
  Site_List := TStringlist.Create;
  SiteStr := TStringlist.Create;
  try
    Ann := '';
    Site_List.Text:=SiteList;
    For i := 0 to Site_List.Count -1 do
    begin
      SiteStr.CommaText := Site_List.Strings[i];
      L := inttostr(Round(Strtofloat(SiteStr[0])/2.54*Dpi)+UpPointL.x);
      T := inttostr(Round(Strtofloat(SiteStr[1])/2.54*Dpi)+UpPointL.y);
      R := inttostr(Round(Strtofloat(SiteStr[2])/2.54*Dpi)+UpPointL.x);
      B := inttostr(Round(Strtofloat(SiteStr[3])/2.54*Dpi)+UpPointL.y);
      TB := strtoint(B) - strtoint(T);
      LR := strtoint(R) - strtoint(L);
      If strtoint(L) < 0 then    //因為十字損壞,抓不到正確的位置,另外做判斷
      begin
        L := '0';
        R := inttostr(LR);
      end;
      if strtoint(T) < 0 then
      begin
        T := '0';
        B := inttostr(TB);
      end;
      if strtoint(R) > ISB.Graphic.Width then
      begin
        R := inttostr(ISB.Graphic.Width);
        L := inttostr(ISB.Graphic.Width-LR);
      end;
      if strtoint(B) > ISB.Graphic.Height then
      begin
        B := inttostr(ISB.Graphic.Height-FixB);
        T := inttostr(ISB.Graphic.Height-FixB-TB);
      end;
      if Mode = 'Mask' then
        Ann := Ann+'(2)('+L+')('+T+')('+R+')('+B+')(1)($FFFFFF)(0)(1)(4)'
      Else if Mode = 'Mark' Then
        Ann := Ann+'(2)('+L+')('+T+')('+R+')('+B+')(1)($00FEFAAD)(1)(1)(4)'
    end;
    If Ann <> '' Then
      Ann := '('+inttostr(Site_List.Count)+')'+Ann;
    ISB.Annotations := Ann;
    If Ann <> '' Then
      ISB.AlwaysShowAnnotations:= true;
  Finally
  Site_List.Free;
  SiteStr.Free;
  end;
end;
procedure SaveAnnotation(ISB:TImageScrollBox;FileName: WideString); //遮罩存檔
Var
  iISB : TImageScrollBox;
begin
  iISB := TImageScrollBox.Create(nil);
  iISB.Graphic := ISB.Graphic;
  iISB.Annotations := ISB.Annotations;
  iISB.AlwaysShowAnnotations := True;
  iISB.MouseMode := mmAnnotate;
  iISB.BurnAnnotations;
  ConvertToBW(iISB.Graphic);
  iISB.SaveToFile(FileName);
  iISB.Free;
end;
Procedure FilterColor(SoISB,DeISB:TImageScrollBox;Ration:Integer); //濾掉顏色 留黑白
var
  x,y : integer;
  xrgb : Trgb;
begin
  DeISB.Graphic.NewImage(SoISB.Graphic.Width,SoISB.Graphic.Height,ifTrueColor, SoISB.Graphic.PalettePtr ,SoISB.Graphic.XDotsPerInch,SoISB.Graphic.YDotsPerInch   );
  For y:= 0 to SoISB.Graphic.Height -1 do
    for x:=0 to SoISB.Graphic.Width -1 do begin
        xrgb := SoISB.Graphic.RGB[x,y];
        if (xrgb.Red > Ration ) or (xrgb.Green > Ration) or (xrgb.Blue > Ration)  then begin
                xrgb.Red   := 255;
                xrgb.Green := 255;
                xrgb.Blue  := 255;
                DeISB.Graphic.RGB[x,y] := xrgb;
                //ImageScrollBox3.Graphic.RGB[x,y] := ImageScrollBox2.Graphic.RGB[x,y];
                end {else begin
                xrgb.Red    := 255;
                xrgb.Green  := 255;
                xrgb.Blue   := 255;
                ImageScrollBox3.Graphic.RGB[x,y] := xrgb;
                end;}
        end;
  DeISB.Redraw(True);
end;
procedure JpgReSize_Exif(Maxlength,Quality:integer;OldFile,NewFile:String;WaterGraphic:TGraphic;PrintDate:Boolean); //照片縮放包含Exif
var
  ImgData:TImgData;
  Orig,Smaller:tjpegimage;
  buffer:tbitmap;
  IH,IW,TH,TL : Integer;
begin
  IH := 0;
  if WaterGraphic <> nil then
  begin
    IW := 80;
    IH := Round(WaterGraphic.Height*IW/WaterGraphic.Width);
  end;
  TH := IH + 5;
  TL := 5;
  Buffer := tbitmap.Create;
  Orig := tjpegImage.Create;
  Smaller := tjpegimage.create;
  ImgData := TimgData.Create;
  try
    //Orig.LoadFromFile(ExtractFilepath(ImgData.Filename)+'\222.jpg');
    ImgData.BuildList := GenAll;  // on by default anyway
    ImgData.ProcessFile(OldFile);
    Orig.LoadFromFile(ImgData.Filename);
    Orig.DIBNeeded;
    Buffer.PixelFormat := pf24bit;
    IF (Orig.Width >= Orig.Height) and (Orig.Width > MaxLength) Then
    begin
      Buffer.Width := MaxLength;
      Buffer.Height := Round(Orig.Height * MaxLength / Orig.Width);
    end
    Else IF (Orig.Height > Orig.Width) and (Orig.Width > MaxLength) Then
    begin
      Buffer.Height := MaxLength;
      Buffer.Width := Round(Orig.Width * MaxLength / Orig.Height);
    end
    Else
    begin
      Buffer.Height :=  Orig.Height;
      Buffer.Width := Orig.Width;
    end;
    // Simple resize
    Buffer.Canvas.StretchDraw(rect(0,0,Buffer.Width,Buffer.Height),Orig);
    Buffer.Canvas.CopyMode := SRCAND;
    if WaterGraphic <> nil then
    begin
      WaterGraphic.Transparent := True;
      Buffer.Canvas.StretchDraw(Rect(0,0,IW,IH),WaterGraphic);
    end;
    //image1.Picture.Graphic.Transparent := True;
    //Buffer.Canvas.StretchDraw(Rect(0,0,IW,IH),image1.Picture.Graphic);
    IF PrintDate and ImgData.HasExif then
    begin
      Buffer.Canvas.Font.Size := 16;
      Buffer.Canvas.Font.Color := clRed;
      Buffer.Canvas.Pen.Mode := pmMerge;
      Buffer.Canvas.Pen.Style  := psClear;
      Buffer.Canvas.Brush.Style := bsClear;
      Buffer.Canvas.TextOut(TL,TH,imgdata.ExifObj.DateTime);
    end;
    Smaller.Assign(Buffer);
    Smaller.CompressionQuality := Quality; //75;
    Smaller.Compress;
    IF ImgData.HasExif then
    begin
      imgdata.WriteEXIFJpeg(Smaller,NewFile);
    end
    Else
    begin
      Smaller.SaveToFile(NewFile);
    end;
  finally // Cleanup
    Buffer.free;
    Orig.Free;
    SMaller.Free;
    ImgData.Free;
  end;
end;
Function GetExif_CaptureDateTime(FileName : String):String; //取出檔案裡的Exif拍攝日期
var
  ImgData:TImgData;
begin
  Result := '';
  ImgData := TimgData.Create;
  try
    ImgData.BuildList := GenAll;  // on by default anyway
    ImgData.ProcessFile(FileName);
    if ImgData.HasExif then
      Result := ImgData.ExifObj.DateTime;
  finally
  ImgData.Free;
  end;
end;
procedure  SetKeyinRect_New( ISB:TImageScrollBox; SiteStr, SiteStr_Black,FormHeight: String; UpPointL,UpPointR:TPoint);
var Rt:Trect;
  XDpi,YDpi : single;
  x1,y1,x2,y2 : Integer;
  XZoom       : Single;
  YZoom       : Single;
  newZoom     : Single;
  theRect,ckRect : Trect;
  Bmp : TBitmap;
  FAnnotationCanvas : TCanvas;
  i : Integer;
  L,T,R,B : String;
  L1,T1,R1,B1 : String;
  S,S1 : TStringlist;
  FormH : Integer;
  IngronDis : Integer;
begin
  S := TStringlist.Create;
  S1 := TStringlist.Create;
  try
    S.CommaText := Sitestr;
    L := S.Strings[0];
    T := S.Strings[1];
    R := S.Strings[2];
    B := S.Strings[3];
    If SiteStr_Black <> '' Then
    begin
      S1.CommaText := SiteStr_Black;
      L1 := S1.Strings[0];
      T1 := S1.Strings[1];
      R1 := S1.Strings[2];
      B1 := S1.Strings[3];
    end;
  finally
  S.Free;
  end;
  //If ISB.FileName = '' Then Exit;
  If (ISB.Hint = '') and (ISB.FileName = '') Then Exit;  //因玉山影像放在Memory裡需使用 hint 來記檔名
  XDpi:=ISB.Graphic.XDotsPerInch;
  YDpi:=ISB.Graphic.YDotsPerInch;
   IngronDis := Round(0.8 /2.54 * Xdpi);  //內容與十字最少要有的距離  先訂1公分
  ISB.Refresh;
  If FormHeight <> '' Then
    FormH := Round(strtoFloat(FormHeight)/2.54*Xdpi)+UpPointL.Y
  Else
    FormH := 0;
  //Therect:=Rect(Round(strtoFloat(L)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y,Round(strtoFloat(R)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  //ckRect :=Rect(Round(strtoFloat(L)/2.54*Xdpi)+UpPointL.X-50,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y,Round(strtoFloat(R)/2.54*Xdpi)+UpPointL.X+50,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  ckRect :=Rect(UpPointL.X,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y+5,UpPointR.X,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  //showmessage(inttostr(ckrect.Left)+#13+inttostr(ckrect.Top)+#13+inttostr(ckrect.Right)+#13+inttostr(ckrect.Bottom)+#13+inttostr(Get_OMR(ckRect,ImageScrollBox1.Graphic)));
  //IF (BlackPoint.X >0) and (BlackPoint.Y >0) and (SiteStr_Black <>'') and (FormH > 0) and ((Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y+IngronDis >= FormH) or (Get_OMR(ckRect,ImageScrollBox1.Graphic)<100))   {(Round(strtoFloat(T)/2.54*Xdpi) >= (9.3*Xdpi))} Then
  //begin
    //Therect:=Rect(BlackPoint.X-Round(strtoFloat(L1)/2.54*Xdpi),BlackPoint.Y -Round(strtoFloat(T1)/2.54*Xdpi),BlackPoint.X-Round(strtoFloat(R1)/2.54*Xdpi),BlackPoint.Y-Round(strtoFloat(B1)/2.54*Xdpi));
    //Showmessage(inttostr(BlackPoint.X)+#13+inttostr(BlackPoint.Y)+#13+inttostr(Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y)+'-->'+inttostr(IngronDis)+'-->'+inttostr(FormH)+#13+inttostr(Get_OMR(ckRect,ImageScrollBox1.Graphic)));
  {end
  else
  begin }
    Therect:=Rect(Round(strtoFloat(L)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y,Round(strtoFloat(R)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  //end;
  XZoom   :=  ( (ISB.Width-20)/(Therect.Right - Therect.Left + 0.3*XDpi) ) * 100;
  YZoom   :=  ( (ISB.Height-20)/(Therect.Bottom - Therect.Top + 0.3*YDpi)) * 100;
  { limit the zoom value to 1000 % }
  newZoom := MinFloat(MinFloat(XZoom,100), YZoom) ;
  ISB.ZoomPercent := newZoom ;
  ISB.HorzScrollBar.Position := SafeTrunc(Therect.Left* newZoom /100 )-SafeTrunc(0.3*XDpi* newZoom /100);
  ISB.VertScrollBar.Position := SafeTrunc((Therect.Top) * newZoom /100 )-SafeTrunc(0.2*YDpi* newZoom /100);
  //加入區域底色
  x1 := SafeTrunc(Therect.Left* newZoom /100 )-ISB.HorzScrollBar.Position;
  x2 := SafeTrunc(Therect.Right* newZoom /100 )-ISB.HorzScrollBar.Position;
  y1 := SafeTrunc(Therect.Top* newZoom /100 )-ISB.VertScrollBar.Position;
  y2 := SafeTrunc(Therect.bottom* newZoom /100 )-ISB.VertScrollBar.Position;
  //Application.ProcessMessages;   //當元件時不可亂用這個指令
  ISB.Refresh;
  FAnnotationCanvas := TCanvas.Create;
  FAnnotationCanvas.Handle := GetDC( ISB.Handle );
  FAnnotationCanvas.Pen.Style := psSolid;
  FAnnotationCanvas.Pen.Color := $00FEFAAD;  //&H80000005//65535;
  FAnnotationCanvas.Pen.Width := 1;
  FAnnotationCanvas.Pen.Mode := pmMask;
  { Draw box on screen }
  for i := y1 to y2 do begin
    FAnnotationCanvas.MoveTo( x1, i );
    FAnnotationCanvas.LineTo( x2, i );
  end;
  ReleaseDC(0,FAnnotationCanvas.Handle);
  FAnnotationCanvas.Free;
end;
procedure SetKeyinRect_New( ISB:TImageScrollBox; SiteStr, SiteStr_Black,FormHeight: String; UpPointL,UpPointR:TPoint;SP:TShape); overload;
var Rt:Trect;
  XDpi,YDpi : single;
  x1,y1,x2,y2 : Integer;
  XZoom       : Single;
  YZoom       : Single;
  newZoom     : Single;
  theRect,ckRect : Trect;
  Bmp : TBitmap;
  FAnnotationCanvas : TCanvas;
  i : Integer;
  L,T,R,B : String;
  L1,T1,R1,B1 : String;
  S,S1 : TStringlist;
  FormH : Integer;
  IngronDis : Integer;
begin
  S := TStringlist.Create;
  S1 := TStringlist.Create;
  try
    S.CommaText := Sitestr;
    L := S.Strings[0];
    T := S.Strings[1];
    R := S.Strings[2];
    B := S.Strings[3];
    If SiteStr_Black <> '' Then
    begin
      S1.CommaText := SiteStr_Black;
      L1 := S1.Strings[0];
      T1 := S1.Strings[1];
      R1 := S1.Strings[2];
      B1 := S1.Strings[3];
    end;
  finally
  S.Free;
  end;
  //If ISB.FileName = '' Then Exit;
  if (ISB.Hint = '') and (ISB.FileName = '') then Exit;  //因玉山影像放在Memory裡需使用 hint 來記檔名
  XDpi:=ISB.Graphic.XDotsPerInch;
  YDpi:=ISB.Graphic.YDotsPerInch;
   IngronDis := Round(0.8 /2.54 * Xdpi);  //內容與十字最少要有的距離  先訂1公分
  ISB.Refresh;
  If FormHeight <> '' Then
    FormH := Round(strtoFloat(FormHeight)/2.54*Xdpi)+UpPointL.Y
  Else
    FormH := 0;
  //Therect:=Rect(Round(strtoFloat(L)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y,Round(strtoFloat(R)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  //ckRect :=Rect(Round(strtoFloat(L)/2.54*Xdpi)+UpPointL.X-50,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y,Round(strtoFloat(R)/2.54*Xdpi)+UpPointL.X+50,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  ckRect :=Rect(UpPointL.X,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y+5,UpPointR.X,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  //showmessage(inttostr(ckrect.Left)+#13+inttostr(ckrect.Top)+#13+inttostr(ckrect.Right)+#13+inttostr(ckrect.Bottom)+#13+inttostr(Get_OMR(ckRect,ImageScrollBox1.Graphic)));
  //IF (BlackPoint.X >0) and (BlackPoint.Y >0) and (SiteStr_Black <>'') and (FormH > 0) and ((Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y+IngronDis >= FormH) or (Get_OMR(ckRect,ImageScrollBox1.Graphic)<100))   {(Round(strtoFloat(T)/2.54*Xdpi) >= (9.3*Xdpi))} Then
  //begin
    //Therect:=Rect(BlackPoint.X-Round(strtoFloat(L1)/2.54*Xdpi),BlackPoint.Y -Round(strtoFloat(T1)/2.54*Xdpi),BlackPoint.X-Round(strtoFloat(R1)/2.54*Xdpi),BlackPoint.Y-Round(strtoFloat(B1)/2.54*Xdpi));
    //Showmessage(inttostr(BlackPoint.X)+#13+inttostr(BlackPoint.Y)+#13+inttostr(Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y)+'-->'+inttostr(IngronDis)+'-->'+inttostr(FormH)+#13+inttostr(Get_OMR(ckRect,ImageScrollBox1.Graphic)));
  {end
  else
  begin }
    Therect:=Rect(Round(strtoFloat(L)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(T)/2.54*Xdpi)+UpPointL.Y,Round(strtoFloat(R)/2.54*Xdpi)+UpPointL.X,Round(strtoFloat(B)/2.54*Xdpi)+UpPointL.Y);
  //end;
  XZoom   :=  ( (ISB.Width-20)/(Therect.Right - Therect.Left + 0.3*XDpi) ) * 100;
  YZoom   :=  ( (ISB.Height-20)/(Therect.Bottom - Therect.Top + 0.3*YDpi)) * 100;
  { limit the zoom value to 1000 % }
  newZoom := MinFloat(MinFloat(XZoom,100), YZoom) ;
  ISB.ZoomPercent := newZoom ;
  if ISB.ZoomPercent < 100 then
   ISB.AntiAliased := True;
  ISB.HorzScrollBar.Position := SafeTrunc(Therect.Left* newZoom /100 )-SafeTrunc(0.3*XDpi* newZoom /100);
  ISB.VertScrollBar.Position := SafeTrunc((Therect.Top) * newZoom /100 )-SafeTrunc(0.2*YDpi* newZoom /100);
  //加入區域底色
  x1 := SafeTrunc(Therect.Left* newZoom /100 )-ISB.HorzScrollBar.Position;
  x2 := SafeTrunc(Therect.Right* newZoom /100 )-ISB.HorzScrollBar.Position;
  y1 := SafeTrunc(Therect.Top* newZoom /100 )-ISB.VertScrollBar.Position;
  y2 := SafeTrunc(Therect.bottom* newZoom /100 )-ISB.VertScrollBar.Position;
  //Application.ProcessMessages;   //當元件時不可亂用這個指令
  ISB.Refresh;
  sp.Brush.Color :=$00FEFAAD;
  sp.Parent := ISB;
  sp.Pen.Style := psSolid;
  sp.Pen.Color := $00FEFAAD;
  sp.Pen.Width := 1;
  sp.Pen.Mode := pmMask;
  sp.Left := x1;
  sp.Top := y1;
  sp.Width := x2-x1;
  sp.Height := y2-y1;
  {FAnnotationCanvas := TCanvas.Create;
  FAnnotationCanvas.Handle := GetDC( ISB.Handle );
  FAnnotationCanvas.Pen.Style := psSolid;
  FAnnotationCanvas.Pen.Color := $00FEFAAD;  //&H80000005//65535;
  FAnnotationCanvas.Pen.Width := 1;
  FAnnotationCanvas.Pen.Mode := pmMask;}
  { Draw box on screen }
  {for i := y1 to y2 do begin
    FAnnotationCanvas.MoveTo( x1, i );
    FAnnotationCanvas.LineTo( x2, i );
  end;
  ReleaseDC(0,FAnnotationCanvas.Handle);
  FAnnotationCanvas.Free; }
end;
Procedure Image_Smooth(Graphic:TDibGraphic);
var
    Transform : TSmoothTransform;
    FUndoGraphic : TDibGraphic;
begin
  FUndoGraphic := TDibGraphic.Create;
  FUndoGraphic.Assign(Graphic);
  Transform := TSmoothTransform.Create;
  try
    Transform.ApplyOnDest(FUndoGraphic,Graphic);
  finally
    Transform.Free;
    FUndoGraphic.Free;
  end;
end;
///  FileName 傳入多筆換行字串
procedure PrintImg(FileName, LoginID, Datetime,Path: WideString;WaterBmp:TBitmap);
var
  PrintMode      : TEnvisionPrintMode;
  GraphicPrinter : TDibGraphicPrinter;
  PrtDialog : TPrintDialog;
  S : TStringlist;
  i,Pages,Page : Integer;
  Prt_String : String;
  Prt_H : Integer;
  ISB : TImageScrollBox;
  procedure PrintWithManualPrintJob(LoginID,DateTime:String;Pages,Page:Integer);
  begin
      If Page = 1 Then
      begin
        { if UsePrintJob is False, Printer.BeginDoc and Printer.EndDoc must be
          called by the user. This allows printing multiple images in the
          same job (or page). }
        GraphicPrinter.UsePrintJob := False;
        { if UsePrintJob is False, the print job name that appears in the
          print manager must be specified in using the Title property of the
          Printer object. Otherwise, if UsePrintJob is True, the Title
          property of the TDibGraphicPrinter object is used to specify the
          job name. }
        Printer.Title := '影像列印';
      end;
      IF (Page mod 2) = 1 Then
        Printer.BeginDoc
      Else
        Printer.NewPage;
      ISB.Graphic.Canvas.Font.PixelsPerInch := ISB.Graphic.XDotsPerInch;
      ISB.Graphic.Canvas.Font.Size := 10;
      ISB.Graphic.Canvas.TextOut(20,20, 'Print User:'+LoginID+' '+'Date:'+DateTime);
      GraphicPrinter.Print(ISB.Graphic);
      { this shows how to print text on a page.
      Printer.Canvas.TextOut(10,10, 'Envision Image Library');
      }
      If ((Page mod 2) = 0) or (Page = pages) Then
        Printer.EndDoc;
  end;
  procedure PrintWithAutoPrintJob;
  begin
      GraphicPrinter.UsePrintJob := True;
      GraphicPrinter.Title       := '影像列印';
      GraphicPrinter.Print(ISB.Graphic);
  end;
begin
  ISB := TImageScrollBox.Create(nil);
  S := TStringlist.Create;
  GraphicPrinter := TDibGraphicPrinter.Create;
  PrtDialog := TPrintDialog.Create(nil);
  try
    IF PrtDialog.Execute Then
    begin
      S.Text := FileName;
      Pages := S.Count;
      for i := 0 to S.Count -1 do
      begin
        ISB.LoadFromFile(Path+S.Strings[i],1);
        if WaterBmp <> nil then
          watermark2(WaterBmp,70,'',ISB.Graphic);
        PrintWithManualPrintJob(LoginID,DateTime,Pages,i+1);
      end;
    end;
  Finally
  ISB.Free;
  PrtDialog.Free;
  GraphicPrinter.Free;
  S.Free;
  end;
end;
// Spec_Page :指定分批頁數,傳0則所有影像一次傳送
procedure PrintImg(FileName, LoginID, Datetime,Path: WideString;WaterBmp:TBitmap;Spec_Page:Integer);
var
  PrintMode      : TEnvisionPrintMode;
  GraphicPrinter : TDibGraphicPrinter;
  PrtDialog : TPrintDialog;
  S : TStringlist;
  i,Pages,Page : Integer;
  Prt_String : String;
  Prt_H : Integer;
  ISB : TImageScrollBox;
  procedure PrintWithManualPrintJob(LoginID,DateTime:String;Pages,Page:Integer);
  begin
      If Page = 1 Then
      begin
        { if UsePrintJob is False, Printer.BeginDoc and Printer.EndDoc must be
          called by the user. This allows printing multiple images in the
          same job (or page). }
        GraphicPrinter.UsePrintJob := False;
        { if UsePrintJob is False, the print job name that appears in the
          print manager must be specified in using the Title property of the
          Printer object. Otherwise, if UsePrintJob is True, the Title
          property of the TDibGraphicPrinter object is used to specify the
          job name. }
        Printer.Title := '影像列印';
      end;
      IF ((Spec_Page > 0) and ((Page mod Spec_Page) = 1)) or ((Spec_Page=0) and (page=1))  Then
        Printer.BeginDoc
      Else
        Printer.NewPage;
      ISB.Graphic.Canvas.Font.PixelsPerInch := ISB.Graphic.XDotsPerInch;
      ISB.Graphic.Canvas.Font.Size := 10;
      ISB.Graphic.Canvas.TextOut(20,20, 'Print User:'+LoginID+' '+'Date:'+DateTime);
      GraphicPrinter.Print(ISB.Graphic);
      { this shows how to print text on a page.
      Printer.Canvas.TextOut(10,10, 'Envision Image Library');
      }
      If ((Spec_Page > 0) and ((Page mod Spec_Page) = 0)) or (Page = pages) Then
        Printer.EndDoc;
  end;
  procedure PrintWithAutoPrintJob;
  begin
      GraphicPrinter.UsePrintJob := True;
      GraphicPrinter.Title       := '影像列印';
      GraphicPrinter.Print(ISB.Graphic);
  end;
begin
  ISB := TImageScrollBox.Create(nil);
  S := TStringlist.Create;
  GraphicPrinter := TDibGraphicPrinter.Create;
  PrtDialog := TPrintDialog.Create(nil);
  try
    IF PrtDialog.Execute Then
    begin
      S.Text := FileName;
      Pages := S.Count;
      for i := 0 to S.Count -1 do
      begin
        ISB.LoadFromFile(Path+S.Strings[i],1);
        if WaterBmp <> nil then
          watermark2(WaterBmp,70,'',ISB.Graphic);
        PrintWithManualPrintJob(LoginID,DateTime,Pages,i+1);
      end;
    end;
  Finally
  ISB.Free;
  PrtDialog.Free;
  GraphicPrinter.Free;
  S.Free;
  end;
end;
procedure PrintImg(FileName, LoginID, Datetime,Path: WideString;WaterBmp:TBitmap;Spec_Page:Integer;NeedSetup:Boolean); overload;
var
  PrintMode      : TEnvisionPrintMode;
  GraphicPrinter : TDibGraphicPrinter;
  PrtDialog : TPrintDialog;
  S : TStringlist;
  i,Pages,Page : Integer;
  Prt_String : String;
  Prt_H : Integer;
  ISB : TImageScrollBox;
  procedure PrintWithManualPrintJob(LoginID,DateTime:String;Pages,Page:Integer);
  begin
      If Page = 1 Then
      begin
        { if UsePrintJob is False, Printer.BeginDoc and Printer.EndDoc must be
          called by the user. This allows printing multiple images in the
          same job (or page). }
        GraphicPrinter.UsePrintJob := False;
        { if UsePrintJob is False, the print job name that appears in the
          print manager must be specified in using the Title property of the
          Printer object. Otherwise, if UsePrintJob is True, the Title
          property of the TDibGraphicPrinter object is used to specify the
          job name. }
        Printer.Title := '影像列印';
      end;
      IF ((Spec_Page > 0) and ((Page mod Spec_Page) = 1)) or ((Spec_Page=0) and (page=1))  Then
        Printer.BeginDoc
      Else
        Printer.NewPage;
      ISB.Graphic.Canvas.Font.PixelsPerInch := ISB.Graphic.XDotsPerInch;
      ISB.Graphic.Canvas.Font.Size := 10;
      ISB.Graphic.Canvas.TextOut(20,20, 'Print User:'+LoginID+' '+'Date:'+DateTime);
      GraphicPrinter.Print(ISB.Graphic);
      { this shows how to print text on a page.
      Printer.Canvas.TextOut(10,10, 'Envision Image Library');
      }
      If ((Spec_Page > 0) and ((Page mod Spec_Page) = 0)) or (Page = pages) Then
        Printer.EndDoc;
  end;
  procedure PrintWithAutoPrintJob;
  begin
      GraphicPrinter.UsePrintJob := True;
      GraphicPrinter.Title       := '影像列印';
      GraphicPrinter.Print(ISB.Graphic);
  end;
begin
  ISB := TImageScrollBox.Create(nil);
  S := TStringlist.Create;
  GraphicPrinter := TDibGraphicPrinter.Create;
  PrtDialog := TPrintDialog.Create(nil);
  try
    IF (not NeedSetup) or (NeedSetup and PrtDialog.Execute) Then
    begin
      S.Text := FileName;
      Pages := S.Count;
      for i := 0 to S.Count -1 do
      begin
        ISB.LoadFromFile(Path+S.Strings[i],1);
        if WaterBmp <> nil then
          watermark2(WaterBmp,70,'',ISB.Graphic);
        PrintWithManualPrintJob(LoginID,DateTime,Pages,i+1);
      end;
    end;
  Finally
  ISB.Free;
  PrtDialog.Free;
  GraphicPrinter.Free;
  S.Free;
  end;
end;
Procedure Color2tif(Graphic:TObject;FileName:String); //彩色影像存Tif
var
  TiffGraphic :TTiffGraphic;
begin
  TiffGraphic := TTiffGraphic.Create;
  try
    if Graphic is TTiffGraphic then
      TiffGraphic.Assign(TTiffGraphic(Graphic))
    Else if Graphic is TDibGraphic then
      TiffGraphic.Assign(TDibGraphic(Graphic))
    else
      Showmessage('Format Error');
    if TiffGraphic.ImageFormat = ifBlackWhite then
    begin
      TiffGraphic.SaveToFile(FileName);
    end
    else
    begin
      TiffGraphic.Compression := tcJPEG;
      TiffGraphic.JpegQuality := 30;
      TiffGraphic.SaveToFile(FileName);
    end
  finally
  TiffGraphic.Free;
  end;
end;
end.
reassemble/CB_IMGPSScanImp.api.pas
@@ -556,7 +556,7 @@
{ ==============================================================================
  方法名稱:GetOMRCheckSet
  引用相依:FileExists, LoadFromFile, SaveToFile, dnFile_Get, dnFile
  引用相依:FileExists, LoadFromFile, SaveToFile, dnFile, dnFile_Get
  方法描述:從伺服器下載並更新 OMR 檢核設定檔(OMRSet.zip)。程序會檢查本地 LastDat
            eTime.dat 取得最後更新時間,並發送請求。若伺服器有新資料,則下載後解壓
            縮並更新本地時間戳記;若無更新則維持現狀,並包含完整的錯誤處理邏輯。
@@ -623,7 +623,7 @@
{ ==============================================================================
  方法名稱:GetKeyinSet
  引用相依:FileExists, LoadFromFile, SaveToFile, dnFile_Get, dnFile
  引用相依:FileExists, LoadFromFile, SaveToFile, dnFile, dnFile_Get
  方法描述:從伺服器下載並更新登打設定檔(KeyinSet.zip)。運作機制與 GetOMRCheckSet
             相同,透過比對時間戳記決定是否執行下載與解壓縮,確保本地的登打定位資
            訊與伺服器同步。
@@ -748,7 +748,7 @@
{ ==============================================================================
  方法名稱:CaseComplete
  引用相依:FileExists, LoadFromFile, En_DecryptionStr_Base64
  引用相依:En_DecryptionStr_Base64, FileExists, LoadFromFile
  方法描述:通知伺服器案件傳送完成。函式會收集案件的各項元數據,包含總頁數、主表單 
            ID、經辦資訊、被保人資料等,並根據不同的業務模式(如 NSCAN, ESCAN)格式化
            發送數據。若包含 OMR 檢核失敗資訊或備註,也會一併封裝傳送,最後根據伺服
reassemble/CB_IMGPSScanImp.docmod.pas
@@ -79,7 +79,7 @@
{ ==============================================================================
  方法名稱:ErrFormtoCurrentForm
  引用相依:LoadFromFile, FileExists, RenameFile, SaveToFile, MoveFile
  引用相依:FileExists, LoadFromFile, MoveFile, RenameFile, SaveToFile
  方法描述:修正案件中歸類錯誤的表單代碼及其關聯檔案。邏輯如下:
            1. 取得錯誤與正確表單對應的文件編號。
            2. 遍歷案件下的所有文件目錄,載入各目錄的影像索引檔 (Context.dat)。
reassemble/CB_IMGPSScanImp.fileOp.pas
@@ -64,7 +64,7 @@
{ ==============================================================================
  方法名稱:DeleteFormCodeFile
  引用相依:FileExists, LoadFromFile, DeleteImageFile, ReSortFileName, SaveToFil
  引用相依:DeleteImageFile, FileExists, LoadFromFile, ReSortFileName, SaveToFil
            e
  方法描述:刪除案件中指定 FormID 的所有文件。讀取 Context.dat 並遍歷,若 FormID 
            匹配則執行刪除(在 ESCAN 模式下會檢查是否非當次掃瞄以保護舊圖)。刪除後
@@ -128,8 +128,8 @@
{ ==============================================================================
  方法名稱:DeleteDocNoFile
  引用相依:DeleteDocNoFile, SaveToFile, ReSortFileName, LoadFromFile, FileExist
            s
  引用相依:DeleteDocNoFile, FileExists, LoadFromFile, ReSortFileName, SaveToFil
            e
  方法描述:刪除指定的文件編號(DocNo)影像。遍歷 ContextList,比對文件編號後執行實
            體刪除與清單移除(含 Context 與 Context_DocNo)。處理完成後執行檔案重新
            排序並重新載入清單。
@@ -163,7 +163,7 @@
{ ==============================================================================
  方法名稱:DeleteDocNoFileForESCAN
  引用相依:DeleteDocNoFile, SaveToFile, LoadFromFile, _DelTree
  引用相依:DeleteDocNoFile, LoadFromFile, SaveToFile, _DelTree
  方法描述:在補件模式 (ESCAN) 下刪除屬於特定文件類型 (DocNo) 的影像。方法會從索
            引清單 (ContextList) 的末尾開始向前遍歷,識別符合目標文件編號或附件名
            稱的圖檔。若該檔案並非預先存在的(ISExistImg 返回 False),則執行實體刪
@@ -245,7 +245,7 @@
{ ==============================================================================
  方法名稱:ReSortFileName
  引用相依:ReSortFileName, FileExists, LoadFromFile, RenameFile, SaveToFile
  引用相依:FileExists, LoadFromFile, ReSortFileName, RenameFile, SaveToFile
  方法描述:執行影像檔案名稱的重新排序與更名。讀取目錄下的 Context.dat,根據目前的
            排列順序為每個檔案產生新的三位數序號前綴,並呼叫 ReNameFile 執行磁碟
            更名,最後同步更新清單內容並儲存。
@@ -281,7 +281,7 @@
{ ==============================================================================
  方法名稱:ReSortFileName_New
  引用相依:ReSortFileName, FileExists, LoadFromFile, RenameFile, SaveToFile
  引用相依:FileExists, LoadFromFile, ReSortFileName, RenameFile, SaveToFile
  方法描述:與 ReSortFileName 類似,執行影像檔案名稱的重新排序,但採用從清單末尾向
            前遍歷的方式執行更名與儲存。
============================================================================== }
@@ -315,7 +315,7 @@
{ ==============================================================================
  方法名稱:ReSortFileName2Scanlist
  引用相依:ReSortFileName, FileExists, LoadFromFile, RenameFile, SaveToFile
  引用相依:FileExists, LoadFromFile, ReSortFileName, RenameFile, SaveToFile
  方法描述:針對 scanlist.dat 檔案內的影像路徑進行序號重排。讀取清單後,依序為檔案
            名稱加上新的序號前綴並存回。
============================================================================== }
reassemble/CB_IMGPSScanImp.lfcycle.pas
@@ -311,7 +311,7 @@
{ ==============================================================================
  方法名稱:Timer1Timer
  引用相依:FJpgCompression, GetLocalAppDir, Str2Dir, _DelTree, FileExists, init
  引用相依:FJpgCompression, FileExists, GetLocalAppDir, Str2Dir, _DelTree, init
            kscan
  方法描述:OCX 元件初始化的核心程序。負責從伺服器下載各類基礎資訊(業務、表單、文件
            、檢核規則、常用片語、系統參數等),並設定本地暫存路徑(ScanTemp)。針對異動
reassemble/CB_IMGPSScanImp.misc.pas
@@ -138,7 +138,7 @@
{ ==============================================================================
  方法名稱:InitExistImgList
  引用相依:LoadFromFile, LoadFileGetMD5
  引用相依:LoadFileGetMD5, LoadFromFile
  方法描述:初始化已存在的影像清單。讀取案件路徑下 Download\Context.dat 的檔案內
            容,計算每個檔案的 MD5 雜湊值並存入 ExistImgList,同時將處理過程記錄至
            日誌檔。
reassemble/CB_IMGPSScanImp.omr.pas
@@ -1,4 +1,72 @@
{ ==============================================================================
  方法名稱:GetSiteOMR
  引用相依:FindPoint, GetSiteOMR, Get_OMR, LoadFromFile
  方法描述:在指定影像文件的特定座標執行 OMR (光學標記辨識)。核心邏輯包含:
            1. 檢查並載入影像檔(若尚未載入),並呼叫 ClearLine 初始化黑白緩衝區。
            2. 讀取影像的 DPI 與寬高資訊。
            3. 使用 CM_Str2Rect 將 Site 字串轉為 TRect 矩陣,並參考定位點 (UpLPoi
            nt) 進行位移計算。
            4. 限制辨識區域座標不超出影像邊界。
            5. 呼叫 Get_OMR 對黑白圖形緩衝區執行辨識並返回結果值(通常為 1 或 0)。
            此方法是自動化資料檢核的重要工具。
============================================================================== }
function TCB_IMGPSScanX.GetSiteOMR(FileName,Site:String;bt: Integer): Integer;
var
  OMRRect : TRect;
  Xdpi,Ydpi : Integer;
  W,H : Integer;
begin
  Result := 0;
//ShowMessage('GetSiteOMR');
  IF (ImageScrollBox1.FileName <> FileName) and (FileName <> '') then
  begin
//ShowMessage('11111'+ImageScrollBox1.FileName+#10#13+FileName);
    ImageScrollBox1.LoadFromFile(FileName,1);
{
ShowMessage('UpLPoint='+IntToStr(UpLPoint.X)+','+IntToStr(UpLPoint.Y)+#10#13+
'UpRPoint='+IntToStr(UpRPoint.X)+','+IntToStr(UpRPoint.Y)+#10#13+
'DownLPoint='+IntToStr(DownLPoint.X)+','+IntToStr(DownLPoint.Y)+#10#13+
'DownRPoint='+IntToStr(DownRPoint.X)+','+IntToStr(DownRPoint.Y));
    FindPoint(ImageScrollBox1.Graphic,UpLPoint,UpRPoint,DownLPoint,'');
ShowMessage('UpLPoint='+IntToStr(UpLPoint.X)+','+IntToStr(UpLPoint.Y)+#10#13+
'UpRPoint='+IntToStr(UpRPoint.X)+','+IntToStr(UpRPoint.Y)+#10#13+
'DownLPoint='+IntToStr(DownLPoint.X)+','+IntToStr(DownLPoint.Y)+#10#13+
'DownRPoint='+IntToStr(DownRPoint.X)+','+IntToStr(DownRPoint.Y));
}
    ClearLine(ISB_BW.Graphic,bt);
    ISB_BW.Redraw(True);
    Application.ProcessMessages;
  end;
  If ImageScrollBox1.FileName <> '' Then
  begin
//ShowMessage('22222'+ImageScrollBox1.FileName);
    Xdpi := ImagescrollBox1.Graphic.XDotsPerInch;
    Ydpi := ImagescrollBox1.Graphic.YDotsPerInch;
    H := ImageScrollBox1.Graphic.Height;
    W := ImageScrollBox1.Graphic.Width;
//ShowMessage('Xdpi='+IntToStr(Xdpi)+#10#13+'Ydpi='+IntToStr(Ydpi)+#10#13+'H='+IntToStr(H)+#10#13+'W='+IntToStr(W)+#10#13);
//ShowMessage('Site='+Site);
    OMRRect := CM_Str2Rect(Site,Xdpi,UpLPoint);
Display1.Lines.Add('UpLPoint=('+IntToStr(UpLPoint.X)+','+IntToStr(UpLPoint.Y)+');'+Site+';'+IntToStr(OMRRect.Left)+','+IntToStr(OMRRect.top)+','+IntToStr(OMRRect.Right)+','+IntToStr(OMRRect.Bottom));
    if OMRRect.Left < 0 then
      OMRRect.Left := 0;
    if OMRRect.Top < 0  then
      OMRRect.Top := 0;
    if OMRRect.Right > ImageScrollBox1.Graphic.Width then
      OMRRect.Right := ImageScrollBox1.Graphic.Width;
    if OMRRect.Bottom > ImageScrollBox1.Graphic.Height then
      OMRRect.Bottom := ImageScrollBox1.Graphic.Height;
    result := Get_OMR(ISB_BW.Graphic,OMRRect);
//ShowMessage('result='+IntToStr(result));
  end;
end;
{ ==============================================================================
  方法名稱:CheckRule2OMRErrInfo
  引用相依:
  方法描述:將檢核規則清單(CHECK_RULE_INF_List)中的資料轉換為內部的 OMRErrInfo R
@@ -36,7 +104,7 @@
{ ==============================================================================
  方法名稱:DistinctFormCode
  引用相依:LoadFromFile, LoadFileGetMD5
  引用相依:LoadFileGetMD5, LoadFromFile
  方法描述:從案件的 Context.dat 檔案中提取不重複的表單代碼(FormCode)並存入 OMRF
            ileList。程序會遍歷上傳目錄下的所有檔案,檢查檔案是否存在且未被處理過。
            接著比對 OMRFileList 中已有的表單代碼,若為新出現的表單代碼且檔案有效
@@ -91,8 +159,8 @@
{ ==============================================================================
  方法名稱:OMRCheckCase
  引用相依:ImageReSize_FormID, GetSiteOMR, FindPoint, LoadFromFile, FileExists,
             SaveToFile
  引用相依:FileExists, FindPoint, GetSiteOMR, ImageReSize_FormID, ImageResize,
            LoadFromFile, SaveToFile
  方法描述:執行案件的自動化 OMR 規則檢核,這是確保掃瞄案件合規性的核心邏輯。程序
            流程:
            1. 初始化檢核日誌。
reassemble/CB_IMGPSScanImp.prUpload.pas
@@ -53,8 +53,8 @@
{ ==============================================================================
  方法名稱:Case2upload
  引用相依:DirectoryExists, _DelTree, Str2Dir, CopyFile, FileExists, LoadFromFi
            le, SaveToFile
  引用相依:CopyFile, DirectoryExists, FileExists, LoadFromFile, SaveToFile, Str
            2Dir, _DelTree
  方法描述:準備案件上傳用的資料夾結構。將分散在各文件子目錄下的影像檔案複製到單
            一的 Upload 目錄下並重新編號檔名,同時產生彙整後的索引檔與附件。
============================================================================== }
@@ -187,7 +187,7 @@
{ ==============================================================================
  方法名稱:Download2Case
  引用相依:FileExists, CopyFile, LoadFromFile, DirectoryExists, SaveToFile
  引用相依:CopyFile, DirectoryExists, FileExists, LoadFromFile, SaveToFile
  方法描述:將下載或匯入的案件結構還原回本系統的多層級目錄結構。包含建立文件子目
            錄、檔案複製並重新編號、以及重建各類索引與份數設定檔。
============================================================================== }
reassemble/bloc/mermaid/BarCode2CaseID.md
@@ -1,13 +1,15 @@
```mermaid
graph TD
    Start([開始 BarCode2CaseID]) --> Init[Result = 空字串]
    Init --> LoopStart{遍歷 MpsBarcodeinf.Count}
    LoopStart -- i=1 to Count --> CheckLen{長度 == CaseIDLength?}
flowchart TD
    Start([開始 BarCode2CaseID]) --> InitResult[初始化 Result 為空字串]
    InitResult --> LoopStart[遍歷條碼清單 i = 1 to Count]
    
    CheckLen -- 是 --> Found[Result = 條碼內容<br/>Break 迴圈]
    CheckLen -- 否 --> LoopNext[下一個條碼]
    LoopNext --> LoopStart
    LoopStart --> CheckLen{條碼長度是否等於 CaseIDLength?}
    CheckLen -- 是 --> SetResult[設定 Result 為此條碼]
    CheckLen -- 否 --> NextIter[下一筆]
    
    Found --> End([結束])
    LoopStart -- 結束 --> End
    SetResult --> BreakLoop[中斷迴圈]
    NextIter --> LoopStart
    BreakLoop --> End([結束])
    LoopStart -- 遍歷完成 --> End
```
reassemble/bloc/mermaid/BarCode2FormID.md
@@ -1,19 +1,24 @@
```mermaid
graph TD
    Start([開始 BarCode2FormID]) --> Init[Result = 空字串]
    Init --> LoopStart{遍歷 MpsBarcodeinf.Count}
    LoopStart -- i=1 to Count --> CheckLen{長度 == FormIDLength?}
flowchart TD
    Start([開始 BarCode2FormID]) --> InitResult[初始化 Result 為空字串]
    InitResult --> LoopStart[遍歷條碼清單 i = 1 to Count]
    
    CheckLen -- 是 --> CheckAppear{FormIDAppear 驗證?}
    CheckLen -- 否 --> LoopNext[下一個條碼]
    LoopStart --> CheckLen{條碼長度是否等於 FormIDLength?}
    CheckLen -- 是 --> SetFormID[設定 FormID 為此條碼]
    CheckLen -- 否 --> NextIter[下一筆]
    
    CheckAppear -- 通過 --> CheckExist{FormIDExists 驗證?}
    CheckAppear -- 不通過 --> ClearForm[FormID = 空] --> LoopNext
    SetFormID --> VerifyAppear{FormIDAppear 驗證?}
    VerifyAppear -- 失敗 --> ClearFormID[清空 FormID]
    VerifyAppear -- 成功 --> CheckExists{FormIDExists 驗證?}
    
    CheckExist -- 通過 --> Found[Result = FormID<br/>Break 迴圈]
    CheckExist -- 不通過 --> LoopNext[下一個條碼]
    ClearFormID --> CheckExists
    
    LoopNext --> LoopStart
    Found --> End([結束])
    LoopStart -- 結束 --> End
    CheckExists -- 有效且 FormID 不為空 --> SetResult[設定 Result 為 FormID]
    CheckExists -- 無效 --> NextIter
    SetResult --> BreakLoop[中斷迴圈]
    NextIter --> LoopStart
    BreakLoop --> End([結束])
    LoopStart -- 遍歷完成 --> End
```
reassemble/bloc/mermaid/GetUseCase.md
@@ -1,12 +1,13 @@
```mermaid
graph TD
    Start([開始 GetUseCase]) --> InitIni[建立 TIniFile: Path + 'UseCase.ini']
    InitIni --> ModeCheck{檢查 Mode}
flowchart TD
    Start([開始 GetUseCase]) --> CreateIni[建立 UseCase.ini]
    CreateIni --> CheckMode{判斷 Mode}
    
    ModeCheck -- 'F' (From) --> ReadFrom[讀取 FROM_CASEID]
    ModeCheck -- 'T' (To) --> ReadTo[讀取 TO_CASEID]
    CheckMode -- 'F' (來源) --> ReadFrom[讀取 FROM_CASEID]
    CheckMode -- 'T' (目標) --> ReadTo[讀取 TO_CASEID]
    
    ReadFrom --> Finally[釋放 TIniFile]
    ReadTo --> Finally
    Finally --> End([返回 Result])
    ReadFrom --> Finalize[釋放 ini 物件]
    ReadTo --> Finalize
    Finalize --> End([返回結果並結束])
```
reassemble/bloc/mermaid/SetUseCase.md
@@ -1,12 +1,13 @@
```mermaid
graph TD
    Start([開始 SetUseCase]) --> InitIni[建立 TIniFile: Path + 'UseCase.ini']
    InitIni --> ModeCheck{檢查 Mode}
flowchart TD
    Start([開始 SetUseCase]) --> CreateIni[建立 UseCase.ini]
    CreateIni --> CheckMode{判斷 Mode}
    
    ModeCheck -- 'A' (Add) --> AddAction[寫入 DocDir 區段:<br/>FROM_CASEID = FormCaseID<br/>TO_CASEID = ToCaseID]
    ModeCheck -- 'D' (Delete) --> DelAction[刪除 DocDir 區段]
    CheckMode -- 'A' (新增) --> AddEntry[寫入 FROM_CASEID 與 TO_CASEID 到 DocDir 區段]
    CheckMode -- 'D' (刪除) --> DelEntry[刪除 DocDir 區段]
    
    AddAction --> Finally[釋放 TIniFile]
    DelAction --> Finally
    Finally --> End([結束])
    AddEntry --> Finalize[釋放 ini 物件]
    DelEntry --> Finalize
    Finalize --> End([結束])
```
reassemble/img/mermaid/CheckNeedCrop.md
@@ -1,22 +1,16 @@
```mermaid
flowchart TD
    Start([開始 CheckNeedCrop]) --> Init[Result = False, FormIDCount = 0]
    Init --> CheckWidth{影像寬度 > 4 * XDPI?}
    Start([開始]) --> GetFormInfo[查詢 FORM_INF_List 取得表單尺寸與定位模式]
    GetFormInfo --> CheckType{是否為 ANCHOR 或 FRAME?}
    
    CheckWidth -- 是 --> LoopStart[遍歷條碼清單 MpsBarcodeinf]
    CheckWidth -- 否 --> FinalCheck
    CheckType -- 是 --> LoadTmp[載入暫存影像檔]
    CheckType -- 否 --> End([結束])
    
    LoopStart --> CheckBarcode{長度正確且表單代碼存在?}
    CheckBarcode -- 是 --> IncCount[FormIDCount + 1]
    CheckBarcode -- 否 --> NextItem
    IncCount --> NextItem{是否還有下一個?}
    LoadTmp --> CheckResize[呼叫 CheckSize 檢查偏移與比例]
    CheckResize --> NeedSave{是否需要儲存調整?}
    
    NextItem -- 是 --> LoopStart
    NextItem -- 否 --> FinalCheck
    NeedSave -- 是 (SizeStr 非空) --> SaveTmp[將調整後的影像覆蓋存回]
    NeedSave -- 否 --> ClearISB[清空顯示路徑]
    
    FinalCheck{FormIDCount 等於 2?}
    FinalCheck -- 是 --> SetTrue[Result = True]
    FinalCheck -- 否 --> End([結束])
    SetTrue --> End
    SaveTmp --> ClearISB --> End
```
reassemble/img/mermaid/GetSiteOMR.md
@@ -1,39 +1,12 @@
```mermaid
flowchart TD
    Start([開始 GetSiteOMR]) --> Init[Result = 0]
    Init --> CheckLoad{影像檔名不符且非空?}
    Start([開始]) --> CheckFile{影像是否已載入?}
    CheckFile -- 否 --> LoadImg[從檔案載入影像並清除緩衝線]
    CheckFile -- 是 --> GetInfo[獲取影像 DPI 與寬高資訊]
    
    CheckLoad -- 是 --> LoadImg[載入影像 ImageScrollBox1.LoadFromFile]
    LoadImg --> ClearBW[初始化黑白緩衝區 ClearLine]
    ClearBW --> Redraw[重繪影像 ISB_BW.Redraw]
    Redraw --> ProcessMsg[處理系統訊息 Application.ProcessMessages]
    ProcessMsg --> CheckFileExist
    CheckLoad -- 否 --> CheckFileExist{ImageScrollBox1.FileName 非空?}
    CheckFileExist -- 是 --> GetInfo[獲取 DPI 與影像寬高]
    GetInfo --> ConvRect[轉換 Site 字串為 OMRRect 矩陣\nCM_Str2Rect]
    ConvRect --> LogDisp[記錄定位點與矩陣資訊到 Display1]
    LogDisp --> BoundaryL{Left < 0?}
    BoundaryL -- 是 --> SetL[Left = 0]
    BoundaryL -- 否 --> BoundaryT{Top < 0?}
    SetL --> BoundaryT
    BoundaryT -- 是 --> SetT[Top = 0]
    BoundaryT -- 否 --> BoundaryR{Right > Width?}
    SetT --> BoundaryR
    BoundaryR -- 是 --> SetR[Right = Width]
    BoundaryR -- 否 --> BoundaryB{Bottom > Height?}
    SetR --> BoundaryB
    BoundaryB -- 是 --> SetB[Bottom = Height]
    BoundaryB -- 否 --> CallOMR
    SetB --> CallOMR
    CallOMR[呼叫 Get_OMR 執行辨識] --> SetResult[Result = 辨識結果]
    SetResult --> End([結束])
    CheckFileExist -- 否 --> End
    LoadImg --> GetInfo
    GetInfo --> CalcRect[將 Site 字串轉為座標矩陣, 並參考定位點位移]
    CalcRect --> BoundCheck[限制座標不超出影像邊界]
    BoundCheck --> GetOMR[呼叫 Get_OMR 執行辨識]
    GetOMR --> ReturnResult[返回辨識結果] --> End([結束])
```
reassemble/img/mermaid/ImageReSize_FormID.md
@@ -1,36 +1,19 @@
```mermaid
flowchart TD
    Start([開始 ImageReSize_FormID]) --> GetFormID[獲取 FormID]
    GetFormID --> CheckID{FormID 為空?}
    CheckID -- 是 --> End([結束])
    CheckID -- 否 --> QueryDB[查詢資料庫獲取表單高寬與定位類型]
    Start([開始]) --> GetFormInfo[查詢 FORM_INF_List 取得高寬與定位類型]
    GetFormInfo --> ValidParam{是否有定位點設定?}
    
    QueryDB --> Found{找到資料?}
    Found -- 否 --> End
    Found -- 是 --> CheckType{定位類型為 ANCHOR/FRAME\n且高寬資訊完整?}
    ValidParam -- 是 --> LoadImg[載入影像檔 (補件模式則跳過)]
    ValidParam -- 否 --> End([結束])
    
    CheckType -- 否 --> End
    CheckType -- 是 --> LoadImg[載入影像檔]
    LoadImg --> FindAnchor[呼叫 FindPoint 尋找十字線或邊框定位點]
    FindAnchor --> CalcResize[呼叫 CheckSize 計算偏移與縮放比例]
    CalcResize --> PerformResize[呼叫 ImageResize 執行影像調整]
    
    LoadImg --> CheckSub{補件模式且圖檔已存在?}
    CheckSub -- 是 --> End
    CheckSub -- 否 --> FindPt1[執行定位點尋找 FindPoint]
    PerformResize --> ResizeOk{縮放是否成功?}
    ResizeOk -- 是 --> SaveImg[儲存影像, 記錄 MD5 與 ReSize.dat 日誌]
    ResizeOk -- 否 (Error) --> LogErr[將錯誤資訊寫入 AnchorError.dat]
    
    FindPt1 --> CalcSize[計算偏移與縮放比例 CheckSize]
    CalcSize --> Resize[執行影像縮放 ImageResize]
    Resize --> FindPt2[縮放後重新定位 FindPoint]
    FindPt2 --> CheckSuccess{SizeStr 有效且非 ERROR?}
    CheckSuccess -- 是 --> CheckMD5{檢查 MD5 是否重複}
    CheckMD5 --> SaveImg[儲存縮放後影像]
    SaveImg --> LogResize[記錄縮放資訊到 ReSize.dat]
    LogResize --> ClearName
    CheckSuccess -- 否 --> CheckError{SizeStr 為 ERROR?}
    CheckError -- 是 --> LogError[記錄錯誤到 AnchorError.dat]
    LogError --> ClearName
    CheckError -- 否 --> ClearName
    ClearName[清除 FileName 快取] --> End
    SaveImg --> ClearISB[清空顯示路徑] --> End
    LogErr --> ClearISB
```
reassemble/img/transformer.pas
@@ -1,7 +1,7 @@
{ ==============================================================================
  方法名稱:ImageReSize_FormID
  引用相依:ImageReSize_FormID, FindPoint, CheckSize, LoadFromFile, SaveToFile,
            FileExists, LoadFileGetMD5
  引用相依:CheckSize, FileExists, FindPoint, ImageReSize_FormID, ImageResize, L
            oadFileGetMD5, LoadFromFile, SaveToFile
  方法描述:根據表單的十字定位點或邊框 (ANCHOR/FRAME) 對掃瞄影像進行縮放調整。流
            程如下:
            1. 從 FORM_INF_List 取得表單預設高寬與定位類型。
@@ -93,7 +93,7 @@
{ ==============================================================================
  方法名稱:ImageReSize_tmp
  引用相依:ImageReSize_tmp, CheckSize, LoadFromFile, SaveToFile
  引用相依:CheckSize, ImageReSize_tmp, ImageResize, LoadFromFile, SaveToFile
  方法描述:針對暫存影像檔案執行十字定位點縮放處理。此方法是 ImageReSize_FormID 
            的簡化版本,主要針對暫存檔 (FileName) 與指定的 FormID。邏輯包含從資料
            庫清單查詢表單尺寸與定位模式,若符合 ANCHOR 或 FRAME 類型,則載入影像
@@ -129,7 +129,7 @@
{ ==============================================================================
  方法名稱:CheckNeedCrop
  引用相依:TDibGraphic, CheckNeedCrop
  引用相依:CheckNeedCrop, TDibGraphic
  方法描述:判斷掃瞄影像是否為 A3 尺寸並需要進行切割(Crop)。判定邏輯有二:首先,檢
            查影像寬度是否大於 4 倍的 DPI 閥值,藉此初步判斷為大尺寸掃瞄件;其次,
            遍歷目前的條碼清單 (MpsBarcodeinf),統計有效的表單代碼 (FormID) 數量。
reassemble/mermaid/lfcycle/DestroyEvent.md
比對新檔案
@@ -0,0 +1,13 @@
```mermaid
flowchart TD
    Start([開始]) --> FreeLists[逐一釋放所有 TStringList 物件資源]
    FreeLists --> SpecialMode{是否為 DSCAN 或 ESCAN?}
    SpecialMode -- 是 --> CheckPath{ImagePath 是否非空?}
    CheckPath -- 是 --> DelTree[執行 _DelTree 刪除暫存影像目錄]
    CheckPath -- 否 --> TriggerEvent
    SpecialMode -- 否 --> TriggerEvent[觸發 COM 介面的 OnDestroy 事件]
    DelTree --> TriggerEvent
    TriggerEvent --> End([結束])
```
reassemble/mermaid/lfcycle/InitialLanguage.md
比對新檔案
@@ -0,0 +1,23 @@
```mermaid
flowchart TD
    Start([開始]) --> LoadIni[讀取 Language.Lng 設定檔]
    LoadIni --> SetGlobal[設定 IISUnit 的語言路徑與語系變數]
    SetGlobal --> LoopComps[遍歷畫面上所有控制項]
    LoopComps --> MatchType{判斷控制項類型}
    MatchType -- Button/Label/GroupBox --> SetCaption[從 INI 讀取對應 Caption 並設定]
    MatchType -- BitBtn/PJMenuBtn --> SetHint[從 INI 讀取對應 Hint 並設定]
    MatchType -- MenuItem --> SetMenuCaption[設定選單項目文字]
    MatchType -- ListView --> SetColCaption[遍歷設定欄位標題]
    MatchType -- RadioGroup --> SetItems[設定標題與選項清單文字]
    SetCaption --> NextComp[處理下一個控制項]
    SetHint --> NextComp
    SetMenuCaption --> NextComp
    SetColCaption --> NextComp
    SetItems --> NextComp
    NextComp --> LoopEnd{所有控制項處理完畢?}
    LoopEnd -- 否 --> LoopComps
    LoopEnd -- 是 --> FreeIni[釋放資源] --> End([結束])
```
reassemble/mermaid/lfcycle/Timer1Timer.md
比對新檔案
@@ -0,0 +1,20 @@
```mermaid
flowchart TD
    Start([開始]) --> InitVars[初始化變數並設定為 False]
    InitVars --> UIConfig[根據 FMode 隱藏/顯示功能按鈕與面板]
    UIConfig --> CallI18n[呼叫 InitialLanguage 載入語系]
    CallI18n --> DataLoading[顯示資料載入中提示]
    DataLoading --> FetchServer[獲取主機時間與範本資訊]
    FetchServer --> FetchInf[分次請求 DOC_INF, FORM_INF, 規則等系統資訊]
    FetchInf --> PathConfig[設定本機暫存路徑與檢核路徑]
    PathConfig --> SpecialMode{是否為 RSCAN/ESCAN?}
    SpecialMode -- 是 --> DownloadCase[下載既有影像並處理舊案轉檔]
    SpecialMode -- 否 --> LoadLocal[載入本地影像檔案]
    DownloadCase --> LoadLocal
    LoadLocal --> SetOk[設定 InitialOk = True 並停止載入提示]
    SetOk --> LogEnd[記錄初始化結束日誌] --> End([結束])
```
reassemble/mermaid/omr/CheckRule2OMRErrInfo.md
@@ -1,24 +1,26 @@
```mermaid
graph TD
    Start([開始 CheckRule2OMRErrInfo]) --> LoopStart{遍歷 i = 1 to 11}
    LoopStart -- I --> FindData[FindSQLData 查詢 CHECK_RULE_INF_List]
flowchart TD
    Start([開始 CheckRule2OMRErrInfo]) --> LoopStart[遍歷檢核規則 i = 1 to 11]
    
    FindData -- 找到資料 --> SetDisplay{MESG_SHOW_TYPE?}
    SetDisplay -- '1' --> DispTrue[OMRErrInfo.Display = True]
    SetDisplay -- '2' --> DispFalse[OMRErrInfo.Display = False]
    LoopStart --> FormNo[產生三位數字編號 CheckNo]
    FormNo --> SearchSQL{在 CHECK_RULE_INF_List 中搜尋?}
    
    DispTrue --> SetIgnore{MESG_DISP_TYPE?}
    SearchSQL -- 找到 --> SetDisplay{判斷顯示類型?}
    SetDisplay -- '1' --> DispTrue[Display = True]
    SetDisplay -- '2' --> DispFalse[Display = False]
    DispTrue --> SetIgnore{判斷忽略類型?}
    DispFalse --> SetIgnore
    
    SetIgnore -- '1' --> IgnTrue[OMRErrInfo.Ignore = True]
    SetIgnore -- '2' --> IgnFalse[OMRErrInfo.Ignore = False]
    SetIgnore -- '1' --> IgnTrue[Ignore = True]
    SetIgnore -- '2' --> IgnFalse[Ignore = False]
    
    IgnTrue --> SetOther[設定 Info = CHECK_MESG<br/>設定 Mode = SCAN_MODE]
    IgnFalse --> SetOther
    IgnTrue --> SetMeta[設定 Info 與 Mode]
    IgnFalse --> SetMeta
    
    SetOther --> LoopNext[下一個 i]
    FindData -- 沒找到 --> LoopNext
    SetMeta --> NextIter[下一個迴圈]
    SearchSQL -- 找不到 --> NextIter
    
    LoopNext --> LoopStart
    NextIter --> LoopStart
    LoopStart -- 結束 --> End([結束])
```
reassemble/mermaid/omr/DistinctFormCode.md
@@ -1,26 +1,22 @@
```mermaid
graph TD
    Start([開始 DistinctFormCode]) --> LoadFile[讀取 Context.dat 到 TStringList]
    LoadFile --> LoopFile{遍歷檔案清單}
flowchart TD
    Start([開始 DistinctFormCode]) --> LoadContext[讀取 Context.dat 到字串清單 S]
    LoadContext --> LoopStart[遍歷清單 i = 0 to Count-1]
    
    LoopFile -- i --> CheckExist{影像是否存在或已縮放?}
    CheckExist -- 是 --> Skip[Continue 下一個]
    CheckExist -- 否 --> GetForm[提取檔案的 FormCode]
    LoopStart --> CheckCat{FWH_category 為 'N'?}
    CheckCat -- 是 --> CheckExist{檔案是否存在或已在 Resize 清單?}
    CheckExist -- 是 --> Continue[跳過此筆]
    CheckExist -- 否 --> ProcessFile
    CheckCat -- 否 --> ProcessFile
    
    GetForm --> LoopOMR{遍歷 OMRFileList}
    LoopOMR -- n --> CompareForm{FormCode 是否重複?}
    CompareForm -- 是 --> MarkRepeat[AddOk = False, Break]
    CompareForm -- 否 --> NextOMR[下一個 OMR 項目]
    NextOMR --> LoopOMR
    Continue --> NextIter
    ProcessFile[提取 FormCode] --> CheckDup{OMRFileList 中是否已存在?}
    
    MarkRepeat --> CheckAdd{AddOk?}
    NextOMR -- 結束 --> CheckAdd
    CheckDup -- 否 --> AddToList[加入 OMRFileList]
    CheckDup -- 是 --> NextIter
    
    CheckAdd -- True --> AddList[加入 OMRFileList]
    CheckAdd -- False --> NextFile[下一個檔案]
    AddToList --> NextIter[下一筆檔案]
    NextIter --> LoopStart
    
    AddList --> NextFile
    Skip --> NextFile
    NextFile --> LoopFile
    LoopFile -- 結束 --> End([結束])
    LoopStart -- 結束 --> End([釋放 S 並結束])
```
reassemble/mermaid/omr/OMRCheckCase.md
@@ -1,31 +1,33 @@
```mermaid
graph TD
    Start([開始 OMRCheckCase]) --> Init[刪除舊日誌/初始化變數]
    Init --> GetMainID[獲取 MainFormID]
flowchart TD
    Start([開始 OMRCheckCase]) --> Init[刪除暫存檔並初始化變數]
    Init --> GetMainID[取得主文件 ID]
    
    GetMainID --> MainCheck{MainFormID 是否存在?}
    MainCheck -- 否 --> Phase2[進入檔案清單檢查]
    MainCheck -- 是 --> CheckDep[檢查主要文件頁數 / 相依文件 / 互斥文件]
    GetMainID --> CaseMain{主文件 ID 是否存在?}
    
    CheckDep --> Phase2
    Phase2 --> LoopContext[遍歷 Context.dat 檢查停用文件]
    CaseMain -- 是 --> CheckMajor[執行主要文件頁數檢核]
    CheckMajor --> CheckDeps[執行相依與互斥文件檢核]
    CheckDeps --> CheckDisabled
    
    LoopContext --> MaxPage[檢查各表單是否超過最大頁數]
    MaxPage --> LoopOMRFiles{遍歷 OMRFileList 進行欄位檢核}
    CaseMain -- 否 --> CheckDisabled[檢查所有表單是否停用]
    
    LoopOMRFiles -- 每張影像 --> Resize[執行十字定位縮放 ImageReSize_FormID]
    Resize --> LoadXML[載入對應 XML 規則]
    CheckDisabled --> MaxPage[檢查表單最大頁數限制]
    MaxPage --> LoopFiles[遍歷 OMRFileList]
    
    LoadXML --> CheckField1[Type 1: 必填欄位檢核]
    CheckField1 --> CheckField3[Type 3: 關聯欄位檢核]
    CheckField3 --> CheckField8[Type 8: 互斥填寫檢核]
    CheckField8 --> CheckField4[Type 4: 欄位有值附文件檢核]
    CheckField4 --> CheckField5[Type 5: 欄位有值不附文件檢核]
    CheckField5 --> CheckField6[Type 6: 欄位有值寫備註檢核]
    CheckField6 --> SaveValue[Type 7: OMR 帶值處理]
    LoopFiles --> Resize[影像十字定位點縮放]
    Resize --> LoadRules[載入對應 XML 規則檔]
    
    SaveValue --> NextOMRFile[下一個影像]
    NextOMRFile --> LoopOMRFiles
    LoadRules --> Rule1[必填欄位檢核 settype1]
    Rule1 --> Rule3[相關欄位關聯檢核 settype3]
    Rule3 --> Rule8[互斥欄位關聯檢核 settype8]
    Rule8 --> Rule4[相依文件檢核 settype4]
    Rule4 --> Rule5[互斥文件檢核 settype5]
    Rule5 --> Rule6[備註檢核 settype6]
    Rule6 --> Rule7[OMR 帶值處理 settype7]
    
    LoopOMRFiles -- 結束 --> Finish([返回 CaseOk 結果])
    Rule7 --> NextFile[下一張影像]
    NextFile --> LoopFiles
    LoopFiles -- 遍歷結束 --> FinalResult[返回 CaseOk 結果]
    FinalResult --> End([結束])
```
reassemble/mermaid/omr/OMRErr2ini.md
@@ -1,10 +1,16 @@
```mermaid
graph TD
    Start([開始 OMRErr2ini]) --> CheckDisp{Display?}
flowchart TD
    Start([開始 OMRErr2ini]) --> CheckDisp{判斷 Display 旗標?}
    
    CheckDisp -- True --> IniWrite[開啟 Checkerr.ini<br/>讀取目前 Count 並累加<br/>寫入 Reason, Site, FileName 等詳細資訊]
    CheckDisp -- False --> DatWrite[開啟 CheckMemo.dat<br/>附加 Reason 錯誤訊息字串]
    CheckDisp -- True (紀錄到 ini) --> OpenIni[開啟 Checkerr.ini]
    OpenIni --> IncCount[取得並遞增 OMRCount]
    IncCount --> WriteDetails[寫入 Reason, Ignore, FileName, Site 等詳細資訊]
    WriteDetails --> CloseIni[關閉並釋放 ini]
    
    IniWrite --> End([結束])
    DatWrite --> End
    CheckDisp -- False (紀錄到 dat) --> OpenDat[讀取 CheckMemo.dat]
    OpenDat --> AddReason[附加 Reason 訊息]
    AddReason --> SaveDat[儲存 CheckMemo.dat]
    CloseIni --> End([結束])
    SaveDat --> End
```
reassemble/mermaid/omr/OMRErrini2List.md
@@ -1,17 +1,20 @@
```mermaid
graph TD
    Start([開始 OMRErrini2List]) --> OpenIni[讀取 Checkerr.ini]
    OpenIni --> LoopErr{遍歷錯誤序號 i = 1 to Count}
flowchart TD
    Start([開始 OMRErrini2List]) --> OpenIni[開啟 Checkerr.ini]
    OpenIni --> GetCount[讀取錯誤總數 Errcount]
    
    LoopErr -- i --> CheckDel{Del == False?}
    CheckDel -- 是 --> AddLV[將 Reason 與索引加入 ListView]
    CheckDel -- 否 --> NextErr[下一個]
    GetCount --> LoopStart[遍歷錯誤 i = 1 to Errcount]
    LoopStart --> CheckDel{是否已被移除 Del?}
    
    AddLV --> NextErr
    NextErr --> LoopErr
    CheckDel -- 否 --> AddToLV[將 Reason 與索引加入 ListView]
    CheckDel -- 是 --> NextIter[下一筆]
    
    LoopErr -- 結束 --> CheckCount{ListView 是否有資料?}
    CheckCount -- 有 --> DisableBt[停用立即上傳按鈕 ImmediateBt]
    CheckCount -- 無 --> End([結束])
    DisableBt --> End
    AddToLV --> NextIter
    NextIter --> LoopStart
    LoopStart -- 結束 --> CheckCount{ListView 是否有資料?}
    CheckCount -- 是 --> DisableUpload[停用上傳按鈕]
    CheckCount -- 否 --> End
    DisableUpload --> End([結束])
```
reassemble/mermaid/omr/OMRErrini2ListForLog.md
@@ -1,21 +1,25 @@
```mermaid
graph TD
    Start([開始 OMRErrini2ListForLog]) --> LoadCases[載入 CaseList.dat]
    LoadCases --> LoopCases{遍歷每個案件}
flowchart TD
    Start([開始 OMRErrini2ListForLog]) --> Init[初始化字串清單 ST 與 CaseList]
    Init --> LoadCases[從 CaseList.dat 載入所有案件]
    
    LoopCases -- CaseID --> OpenIni[讀取該案件的 Checkerr.ini]
    OpenIni --> LoopErr{遍歷 Count}
    LoadCases --> LoopCases[遍歷案件 I = 0 to Count-1]
    LoopCases --> OpenIni[開啟該案件的 Checkerr.ini]
    OpenIni --> GetCount[讀取錯誤總數 Errcount]
    
    LoopErr -- j --> CheckDel{Del == False?}
    CheckDel -- 是 --> AddToST[將 Reason 加入字串清單]
    CheckDel -- 否 --> NextErr[下一個錯誤]
    GetCount --> LoopErrors[遍歷錯誤 j = 1 to Errcount]
    LoopErrors --> CheckDel{是否已被移除?}
    
    AddToST --> NextErr
    NextErr --> LoopErr
    CheckDel -- 否 --> AddToST[將 Reason 加入 ST]
    CheckDel -- 是 --> NextError[下一筆錯誤]
    
    LoopErr -- 結束 --> NextCase[下一個案件]
    AddToST --> NextError
    NextError --> LoopErrors
    LoopErrors -- 結束 --> NextCase[下一筆案件]
    NextCase --> LoopCases
    
    LoopCases -- 結束 --> Result[返回 ST.Text]
    Result --> End([結束])
    LoopCases -- 結束 --> ReturnText[返回 ST.Text 完整錯誤內容]
    ReturnText --> Cleanup[釋放清單物件]
    Cleanup --> End([結束])
```
reassemble/scan/mermaid/GetDefScanIni.md
@@ -1,23 +1,22 @@
```mermaid
graph TD
    Start([Start GetDefScanIni]) --> SetHardDefaults[設定程式內建預設值<br/>Def_DeviceDelete=True, DPI=300 等]
    SetHardDefaults --> LoopList[遍歷 WORK_INF_List]
flowchart TD
    Start([開始]) --> SetHardDefaults[設定硬體預設值: DPI=300, 雙面=True, 旋轉=0 等]
    SetHardDefaults --> LoopList[遍歷 WORK_INF_List 中的參數]
    LoopList --> MatchParam{比對 PARA_NO}
    
    subgraph ParaMatching
        LoopList --> MatchPara{比對 PARA_NO}
        MatchPara -- SCAN_BLANKDEL --> UpdBlank[更新空白頁刪除設定]
        MatchPara -- SCAN_REVERSE --> UpdRev[更新影像反相設定]
        MatchPara -- SCAN_DPI --> UpdDpi[更新 DPI]
        MatchPara -- SCAN_DUPLEX --> UpdDup[更新雙面掃瞄]
        MatchPara -- SCAN_ROTATE --> UpdRot[更新旋轉角度]
        MatchPara -- SCAN_IMGSET --> UpdImgSet[更新亮度對比設定]
        MatchPara -- LOCAL_PATH --> UpdPath[更新 ImagePath]
        MatchPara -- GUIDE/DIV_ID --> UpdFormID[更新導引/分案頁代碼列表]
        MatchPara -- Others --> UpdMisc[更新壓縮比/上傳限制等]
    end
    MatchParam -- 'SCAN_DPI' --> SetDPI[設定 DPI 數值]
    MatchParam -- 'SCAN_DUPLEX' --> SetDuplex[設定是否雙面]
    MatchParam -- 'SCAN_ROTATE_MODE' --> SetRotate[設定旋轉角度]
    MatchParam -- 'LOCAL_PATH' --> SetPath[設定影像儲存路徑]
    MatchParam -- 'GUIDEFORMID' --> SetGuide[設定導引頁代碼列表]
    
    UpdMisc --> LoopNext[下一個參數]
    LoopNext --> LoopList
    LoopList -- 結束 --> FinalSet[ScanDuplex := Def_ScanDuplex]
    FinalSet --> EndGetDef([End])
    SetDPI --> NextItem[下一個參數]
    SetDuplex --> NextItem
    SetRotate --> NextItem
    SetPath --> NextItem
    SetGuide --> NextItem
    NextItem --> LoopEnd{所有參數處理完畢?}
    LoopEnd -- 否 --> LoopList
    LoopEnd -- 是 --> End([結束])
```
reassemble/scan/mermaid/OnAcquire.md
@@ -1,39 +1,31 @@
```mermaid
graph TD
    Start([Start OnAcquire]) --> GetInfo[取得 pScanInfo]
    GetInfo --> MultiPage{MultiPage?}
flowchart TD
    Start([開始]) --> GetInfo[獲取 CallBackData 並轉為 ScanInfo 指針]
    GetInfo --> AssignDIB[將 DIB 句柄轉為影像對象並設定 DPI]
    AssignDIB --> FormatCheck{檢查影像格式}
    
    subgraph ImagePreprocessing
        MultiPage -- Yes --> AssignDib[Assign DIB to Graphic & Set DPI]
        AssignDib --> CheckFormat{ImageFormat?}
        CheckFormat -- BlackWhite --> BWProc[條碼辨識/旋轉/反向/傾斜矯正/清黑邊]
        CheckFormat -- TrueColor --> ColorProc[條碼辨識/旋轉/設定 JPEG 壓縮]
        CheckFormat -- Gray/Color256 --> GrayProc[轉灰階/設定 JPEG 壓縮]
        CheckFormat -- Others --> OtherProc[設定 PackBits 壓縮]
    end
    FormatCheck -- 黑白 (ifBlackWhite) --> BWProc[條碼辨識, 旋轉, 反向, 去偏斜, 清黑邊]
    FormatCheck -- 全彩 (ifTrueColor) --> ColorProc[設定 JPEG 壓縮品質與條碼辨識旋轉]
    FormatCheck -- 灰階 (ifGray256) --> GrayProc[設定 JPEG 壓縮品質與條碼辨識旋轉]
    
    subgraph CropAndSave
        ImagePreprocessing --> CreateTempGr[建立 iGraphic_First, iGraphic_sec]
        CreateTempGr --> CropCheck{需要裁切 A3?}
        CropCheck -- Yes --> DoCrop[將影像裁切為左右兩份]
        CropCheck -- No --> SetFirst[iGraphic := iGraphic_First]
        DoCrop --> SetFirst
        SetFirst --> WhileLoop{iGraphic 不為空?}
        WhileLoop -- Yes --> BlankCheck{是否刪除空白頁?}
        BlankCheck -- No/Valid --> PageProcess[UI 顯示影像<br/>PageEnd 決定路徑與檔名]
        PageProcess --> SaveFile{儲存檔案}
        SaveFile -- .tif --> AppendTif[Append To Stream]
        SaveFile -- .jpg --> SaveJpg[Save To File]
        AppendTif --> DoPageDone[PageDone]
        SaveJpg --> DoPageDone
        DoPageDone --> NextImg[切換至 iGraphic_Sec 或 設為空]
        BlankCheck -- IsBlank --> NextImg
        NextImg --> WhileLoop
    end
    BWProc --> CropCheck{檢查是否需 A3 切割為 A4?}
    ColorProc --> CropCheck
    GrayProc --> CropCheck
    
    WhileLoop -- No --> FinallyFree[釋放暫存 Graphic 資源]
    FinallyFree --> EndOnAcquire([End])
    CropCheck -- 是 --> PerformCrop[執行影像切割為第一與第二部分]
    CropCheck -- 否 --> SingleImg[直接處理原始影像]
    PerformCrop --> LoopPages[對每一部分進行迴圈處理]
    SingleImg --> LoopPages
    LoopPages --> BlankCheck{是否刪除空白頁?}
    BlankCheck -- 否/非空白 --> SaveProc[呼叫 PageEnd 決定路徑檔名並儲存檔案]
    BlankCheck -- 是空白 --> NextPage[處理下一部分]
    SaveProc --> CallPageDone[呼叫 PageDone 更新 UI]
    CallPageDone --> NextPage
    NextPage --> LoopEnd{所有部分處理完畢?}
    LoopEnd -- 否 --> LoopPages
    LoopEnd -- 是 --> FreeRes[釋放資源] --> End([結束])
```
reassemble/scan/mermaid/PageDone.md
@@ -1,21 +1,15 @@
```mermaid
graph TD
    Start([Start PageDone]) --> IncCount[Scaninfo.ImageCount 遞增]
    IncCount --> CaseMode{ScanMode?}
flowchart TD
    Start([開始]) --> IncCount[累加掃描影像計數]
    IncCount --> ModeCheck{檢查掃描模式}
    
    CaseMode -- smNew --> NewMode[根據 ScanImgShowMode 設定 ISB]
    NewMode --> ShowType{顯示模式?}
    ShowType -- 清楚/模糊 --> LoadNew[ISB.LoadFromFile<br/>PEFileName]
    ShowType -- 不顯示 --> smReplace
    ModeCheck -- 新案 (smNew) --> NewMode[根據顯示模式尋找 ISB 並載入影像]
    ModeCheck -- 取代 (smReplace) --> ReplaceMode[將影像載入當前顯示的 ISB]
    ModeCheck -- 插入 (smInsert) --> InsertMode[尋找 ISB 並以符合頁面模式載入影像]
    ModeCheck -- 範本 (smSample) --> SampleMode[尋找 ISB 並載入影像]
    
    CaseMode -- smReplace --> LoadRep[DisplayISB.LoadFromFile]
    CaseMode -- smInsert --> LoadIns[ISB.LoadFromFile]
    CaseMode -- smSample --> LoadSam[ISB.LoadFromFile]
    CaseMode -- smRTS --> EndPageDone
    LoadNew --> EndPageDone
    LoadRep --> EndPageDone
    LoadIns --> EndPageDone
    LoadSam --> EndPageDone
    EndPageDone([End])
    NewMode --> End([結束])
    ReplaceMode --> End
    InsertMode --> End
    SampleMode --> End
```
reassemble/scan/mermaid/PageEnd.md
@@ -1,38 +1,20 @@
```mermaid
graph TD
    Start([Start PageEnd]) --> CaseMode{ScanMode?}
flowchart TD
    Start([開始]) --> ModeCheck{檢查掃描模式}
    
    subgraph smNew_Logic
        CaseMode -- smNew --> GetFormID[獲取 FormID / 導引頁 / 分案頁資訊]
        GetFormID --> IsDiv{偵測到分案條碼?}
        IsDiv -- Yes --> ResetCase[重置計數/清空 View/取得新 ScanCaseno]
        IsDiv -- No --> CheckCaseNo{ScanCaseno 為空?}
        CheckCaseNo -- Yes --> GetNoName[GetNoNameCase]
        CheckCaseNo -- No --> InitPath[建立目錄 Str2Dir]
        GetNoName --> InitPath
        InitPath --> DocDir[決定 ScanDocDir<br/>處理分份數邏輯]
        DocDir --> SetDocList[SetDocNoList 更新目錄索引]
        SetDocList --> GenFileName[產生 ScanSaveFilename 序號_FormID]
        GenFileName --> SaveCheck{不存檔條碼?}
        SaveCheck -- No --> SetContext[SetContextList<br/>更新 TreeView UI<br/>設定 PEFileName]
    end
    ModeCheck -- 新案 (smNew) --> NewLogic[取得 FormID 與 DocNo, 處理分案/分份邏輯]
    ModeCheck -- 插入 (smInsert) --> InsertLogic[取得 FormID 並判斷目標文件目錄]
    ModeCheck -- 取代 (smReplace) --> ReplaceLogic[設定替換檔名與路徑]
    ModeCheck -- 範本 (smSample) --> SampleLogic[設定範本儲存路徑]
    
    CaseMode -- smReplace --> DelOld[刪除舊檔] --> SetPE[設定 PEFileName]
    NewLogic --> SubNew{是否偵測到分案頁?}
    SubNew -- 是 --> ResetCase[重置計數並取得新案號, 更新 TreeView]
    SubNew -- 否 --> CalcPath[計算儲存目錄與檔名, 更新 ContextList]
    
    subgraph smInsert_Logic
        CaseMode -- smInsert --> GetFormIDIns[獲取 FormID / DocNo]
        GetFormIDIns --> DocDirIns[尋找最後的 DocDir]
        DocDirIns --> GenFileNameIns[產生 ScanSaveFilename]
        GenFileNameIns --> SetContextIns[SetContextList<br/>設定 PEFileName]
    end
    CalcPath --> SetFileName[設定 PEFileName]
    InsertLogic --> SetFileName
    ReplaceLogic --> SetFileName
    SampleLogic --> SetFileName
    
    CaseMode -- smSample --> SamLogic[刪除舊檔/設定 PEFileName/顯示辨識條碼]
    SetContext --> EndPageEnd
    SetPE --> EndPageEnd
    SetContextIns --> EndPageEnd
    SamLogic --> EndPageEnd
    EndPageEnd([End])
    SetFileName --> End([結束])
```
reassemble/scan/mermaid/R_W_Scanini.md
@@ -1,15 +1,13 @@
```mermaid
graph TD
    Start([Start R_W_Scanini]) --> CreateIni[建立 TIniFile: FBScan.ini]
    CreateIni --> TryBlock[try]
flowchart TD
    Start([開始]) --> CreateIni[建立 Tinifile 物件載入 FBScan.ini]
    CreateIni --> ModeCheck{操作模式}
    
    subgraph ModeSwitch
        TryBlock --> ModeCheck{Mode?}
        ModeCheck -- 'R' (Read) --> ReadIni[從 Ini 讀取:<br/>DeviceDelete, Reverse, BoardClear, <br/>Rotate, Deskew, Brightness, Contrast 等]
        ModeCheck -- 'W' (Write) --> WriteIni[將變數寫入 Ini:<br/>DeviceDelete, Reverse, BoardClear, <br/>Rotate, Deskew, Brightness, Contrast 等]
    end
    ModeCheck -- 'R' 讀取 --> ReadParams[讀取空白頁設定, 反向, 旋轉, 去偏斜, 亮度對比等]
    ModeCheck -- 'W' 寫入 --> WriteParams[將當前參數值寫入設定檔]
    
    ModeSwitch --> FinallyBlock[finally]
    FinallyBlock --> FreeIni[釋放 Ini 物件]
    FreeIni --> EndRW([End])
    ReadParams --> FreeIni[釋放 Tinifile 資源]
    WriteParams --> FreeIni
    FreeIni --> End([結束])
```
reassemble/scan/mermaid/StatrTwainScan.md
@@ -1,35 +1,13 @@
```mermaid
graph TD
    Start([Start StatrTwainScan]) --> CheckConfig{Scanner.IsConfigured?}
    CheckConfig -- No --> MsgNoDriver[顯示 TWAIN 驅動尚未安裝訊息] --> ExitStatrTwainScan([Exit])
    CheckConfig -- Yes --> InitScanInfo[初始化 ScanInfo 結構<br/>建立 TTiffGraphic]
    InitScanInfo --> TryBlock1[try: 開始掃瞄設定]
    subgraph Try_Main
        TryBlock1 --> SetParams[設定 DPI, ScanColor, ShowUI]
        SetParams --> TryOpen[try: 開啟掃瞄來源]
        subgraph Try_OpenSource
            TryOpen --> OpenSource[Scanner.OpenSource]
            OpenSource --> SetDuplex[設定雙面/亮度/對比]
        end
        SetDuplex --> CatchOpen{發生錯誤?}
        CatchOpen -- Yes --> ShowErr[顯示掃瞄器錯誤]
        ShowErr --> CloseSrc1[Scanner.CloseSource] --> ExitStatrTwainScan
        CatchOpen -- No --> TryAcquire[try: 執行獲取影像]
        subgraph Try_Acquire
            TryAcquire --> Acquire[Scanner.AcquireWithSourceOpen<br/>傳入 OnAcquire 回呼]
            Acquire --> CatchAcq{發生 Exception?}
            CatchAcq -- Yes --> CloseSrc2[Scanner.CloseSource]
        end
        TryAcquire -. finally .-> CloseSrc3[Scanner.CloseSource]
    end
    Try_Main -. finally .-> FinalCleanup[Scanner.CloseSource<br/>ScanInfo.Graphic.Free]
    FinalCleanup --> EndStatrTwainScan([End])
flowchart TD
    Start([開始]) --> CheckConfig{掃描器驅動是否已安裝?}
    CheckConfig -- 否 --> ShowErr[顯示錯誤訊息] --> End([結束])
    CheckConfig -- 是 --> InitScanInfo[初始化 ScanInfo 並設定為多頁模式]
    InitScanInfo --> SetScannerParams[設定掃描參數: DPI, 格式, UI 顯示模式]
    SetScannerParams --> OpenSource[開啟掃描來源]
    OpenSource --> TryAcquire[執行 AcquireWithSourceOpen 掃描作業]
    TryAcquire --> CatchErr{掃描過程中發生錯誤?}
    CatchErr -- 是 --> ShowScanErr[顯示掃描錯誤訊息] --> CloseSource1[關閉掃描來源] --> FreeRes[釋放 Graphic 資源] --> End
    CatchErr -- 否 --> CloseSource2[掃描結束, 關閉掃描來源]
    CloseSource2 --> FreeRes --> End
```
reassemble/scan/mermaid/initkscan.md
@@ -1,21 +1,17 @@
```mermaid
graph TD
    Start([Start initkscan]) --> DisableCB[ScanDuplexCB.Enabled := False]
    DisableCB --> CheckConfig{Scanner.IsConfigured?}
flowchart TD
    Start([開始]) --> DisableUI[將雙面掃描勾選框設為停用]
    DisableUI --> CheckConfig{掃描器是否已設定?}
    
    CheckConfig -- Yes --> TryOpen[try]
    CheckConfig -- 是 --> TryOpen[嘗試開啟掃描來源 (OpenSource)]
    CheckConfig -- 否 --> End([結束])
    
    subgraph Try_Detect
        TryOpen --> OpenSrc[Scanner.OpenSource]
        OpenSrc --> CheckDuplex{Scanner.DuplexCap > 0?}
        CheckDuplex -- Yes --> EnableCB[ScanDuplexCB.Enabled := True]
        CheckDuplex -- No --> CatchErr
        EnableCB --> CatchErr{發生異常?}
        CatchErr -- Yes --> DataLoadErr[DataLoading False, True] --> ExitInit
    end
    TryOpen --> CheckDuplex{硬體是否支援雙面?}
    CheckDuplex -- 是 (DuplexCap > 0) --> EnableUI[啟用雙面掃描勾選框]
    CheckDuplex -- 否 --> CloseSource[關閉掃描來源]
    
    Try_Detect --> CloseSrc[Scanner.CloseSource]
    CheckConfig -- No --> EndInit
    CloseSrc --> EndInit
    EndInit([End])
    EnableUI --> CloseSource
    CloseSource --> End
    TryOpen -- 異常 --> StopLoading[呼叫 DataLoading 停止載入提示] --> End
```
reassemble/scan/twain.pas
@@ -1,7 +1,7 @@
{ ==============================================================================
  方法名稱:StatrTwainScan
  引用相依:TTiffGraphic, StatrTwainScan, Scanner, Scanner.OpenSource, Scanner.C
            loseSource, OnAcquire, Scanner.AcquireWithSourceOpen
  引用相依:OnAcquire, Scanner, Scanner.AcquireWithSourceOpen, Scanner.CloseSour
            ce, Scanner.OpenSource, StatrTwainScan, TTiffGraphic
  方法描述:啟動 TWAIN 掃瞄流程。此方法首先檢查掃瞄器驅動是否已安裝,接著初始化 Sc
            anInfo 結構並設定掃瞄參數(如 DPI、影像格式、是否顯示 UI、雙面掃瞄模式、
            亮度與對比)。流程中會開啟掃瞄來源,呼叫 AcquireWithSourceOpen 執行實際
@@ -65,7 +65,7 @@
{ ==============================================================================
  方法名稱:OnAcquire
  引用相依:TTiffGraphic, TJpegGraphic, OnAcquire
  引用相依:OnAcquire, TJpegGraphic, TTiffGraphic
  方法描述:掃瞄影像獲取後的回呼處理函數。核心邏輯包含:
            1. 將獲取的 DIB 句柄轉為影像對象並設定 DPI。
            2. 依影像格式執行對應處理:黑白影像會進行條碼辨識、依條碼角度旋轉、影像
@@ -378,8 +378,8 @@
{ ==============================================================================
  方法名稱:PageEnd
  引用相依:GetNoNameCase, DirectoryExists, _DelTree, Str2Dir, SaveToFile, PageE
            nd
  引用相依:DirectoryExists, GetNoNameCase, PageEnd, SaveToFile, Str2Dir, _DelTr
            ee
  方法描述:管理掃瞄影像的儲存路徑與檔案命名規則。主要邏輯如下:
            1. 辨識條碼以取得 FormID,並判斷是否為導引頁或分案頁。
            2. 若偵測到分案頁,會重置計數並嘗試取得新的案件編號(CaseID)。
@@ -661,7 +661,7 @@
{ ==============================================================================
  方法名稱:GetDefScanIni
  引用相依:Rotate, FJpgCompression, Scanner
  引用相依:FJpgCompression, Rotate, Scanner
  方法描述:從資料庫參數清單(WORK_INF_List)初始化並設定掃瞄的系統預設值。流程首先
            設定一組程式內建的預設數值,隨後遍歷 WORK_INF_List 並比對 PARA_NO 關
            鍵字,動態更新包含:空白頁判斷大小、影像是否反相、清黑邊、掃瞄 DPI、雙面掃
@@ -849,7 +849,7 @@
{ ==============================================================================
  方法名稱:initkscan
  引用相依:initkscan, Scanner, Scanner.OpenSource, Scanner.CloseSource
  引用相依:Scanner, Scanner.CloseSource, Scanner.OpenSource, initkscan
  方法描述:初始化並偵測掃瞄器硬體能力。此方法會先將雙面掃瞄勾選框(ScanDuplexCB)
            設為停用,接著嘗試開啟掃瞄來源(OpenSource),檢查掃瞄器是否支援雙面掃瞄
            功能(DuplexCap > 0)。如果硬體支援,則啟用 UI 上的勾選框供使用者選擇。最
reassemble/transp/fileClient.pas
@@ -89,7 +89,7 @@
{ ==============================================================================
  方法名稱:SetFtpInfo
  引用相依:SetFtpInfo, IIS_Ftp
  引用相依:IIS_Ftp, SetFtpInfo
  方法描述:將 FTP 連線參數與事件回呼設定至 FTP 傳輸元件 (IIS_Ftp)。此方法會將先
            前取得並解碼的連線資訊(如 FTP IP、帳號、密碼、路徑、連接埠)指派給 IIS_Ft
            p 物件,並根據協定類型設定是否開啟 SSL/TLS 以及被動模式 (Passive)。此
reassemble/view/mermaid/ActiveFormKeyUp.md
比對新檔案
@@ -0,0 +1,16 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckFocus{文字輸入框取得焦點?}
    CheckFocus -- 否 --> End([結束])
    CheckFocus -- 是 --> CheckISB{是否有選取影像?}
    CheckISB -- 是 --> MatchKey{判斷按鍵}
    CheckISB -- 否 --> End
    MatchKey -- VK_UP (上) --> CallPrior[呼叫 PriorPage 翻頁]
    MatchKey -- VK_DOWN (下) --> CallNext[呼叫 NextPage 翻頁]
    CallPrior --> ScrollUI[同步滾動捲軸更新顯示]
    CallNext --> ScrollUI
    ScrollUI --> End
```
reassemble/view/mermaid/AddAttFileLBClick.md
比對新檔案
@@ -0,0 +1,21 @@
```mermaid
flowchart TD
    Start([開始]) --> SetFilter[設定副檔名過濾為 PDF]
    SetFilter --> OpenDlg[開啟多選檔案對話框]
    OpenDlg -- 使用者取消 --> End([結束])
    OpenDlg -- 執行 --> ShowLoading[顯示載入中提示]
    ShowLoading --> LoopFiles[遍歷選取的檔案]
    LoopFiles --> CheckDup{檔案是否已存在?}
    CheckDup -- 是 --> AskOver[詢問是否覆蓋]
    CheckDup -- 否 --> CopyProc[執行 CopyFile 複製到案件目錄]
    AskOver -- 否 --> NextFile[處理下一個檔案]
    AskOver -- 是 --> DelOld[刪除舊紀錄] --> CopyProc
    CopyProc --> UpdateList[更新附加檔案清單並顯示]
    UpdateList --> NextFile
    NextFile --> LoopFiles
    LoopFiles -- 結束 --> HideLoading[停止載入提示] --> End
```
reassemble/view/mermaid/AddScanBtnClick.md
比對新檔案
@@ -0,0 +1,11 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckCase{是否選取案件?}
    CheckCase -- 否 --> ShowMsg[提示先選案件] --> End([結束])
    CheckCase -- 是 --> SetMode[設定掃描模式為插入模式 smInsert]
    SetMode --> CallScan[呼叫 StatrTwainScan 執行追加掃描]
    CallScan --> Redraw[重新繪製樹狀結構並更新筆數]
    Redraw --> ClearLog[清空該案檢核記錄]
    ClearLog --> AutoFocus[自動選取新文件節點並顯示] --> End
```
reassemble/view/mermaid/DesableImage.md
比對新檔案
@@ -0,0 +1,10 @@
```mermaid
flowchart TD
    Start([開始]) --> ResetClick[重置 NowClick 點選狀態為 -1]
    ResetClick --> LoopFC[遍歷功能按鈕 FC0 至 FC6]
    LoopFC --> GetGray[從 ImageList 獲取灰階圖示並指派給按鈕]
    GetGray --> NextFC[下一個按鈕]
    NextFC --> LoopFC
    LoopFC -- 結束 --> ResetMouse[將所有 ISB 設為一般使用者模式 mmUser]
    ResetMouse --> End([結束])
```
reassemble/view/mermaid/DisplayMode.md
比對新檔案
@@ -0,0 +1,15 @@
```mermaid
flowchart TD
    Start([開始]) --> HideAll[隱藏所有影像面板 imgp1-imgp8]
    HideAll --> CalcSize[根據 BasePanel 寬高與網格行列計算面板尺寸]
    CalcSize --> LoopGrid[雙重迴圈處理行列排列]
    LoopGrid --> Position[計算每個面板的 Left 與 Top 座標]
    Position --> ShowPl[顯示對應面板並設定寬高與位置]
    ShowPl --> NextGrid[下一個網格位置]
    NextGrid --> LoopGrid
    LoopGrid -- 結束 --> AdjustShape[調整標記框 Shape1 大小並顯示]
    AdjustShape --> UpdateBtn[更新工具列模式圖示]
    UpdateBtn --> FocusISB[初始化首個視窗焦點 ISB1] --> End([結束])
```
reassemble/view/mermaid/DocNoIsExistImg.md
比對新檔案
@@ -0,0 +1,11 @@
```mermaid
flowchart TD
    Start([開始]) --> InitRes[初始化結果為 False]
    InitRes --> LoadCtx[讀取目錄中的 Context.dat]
    LoadCtx --> LoopImgs[遍歷清單中的所有檔名]
    LoopImgs --> CheckExist{實際檔案是否存在?}
    CheckExist -- 是 --> SetFound[發現影像, 返回 False] --> End([結束])
    CheckExist -- 否 --> NextImg[檢查下一個檔案]
    NextImg --> LoopImgs
    LoopImgs -- 結束且未發現 --> SetEmpty[返回 True, 表示無影像] --> End
```
reassemble/view/mermaid/EnableImage.md
比對新檔案
@@ -0,0 +1,8 @@
```mermaid
flowchart TD
    Start([開始]) --> CallDisable[呼叫 DesableImage 停用既有功能]
    CallDisable --> GetBmp[從 ImageList 獲取指定功能的彩色圖示]
    GetBmp --> SetGlyph[將圖示指派給點選的按鈕]
    SetGlyph --> CallMode[呼叫 ViewMouseMode 切換滑鼠作業模式]
    CallMode --> End([結束])
```
reassemble/view/mermaid/FindISB2View.md
比對新檔案
@@ -0,0 +1,19 @@
```mermaid
flowchart TD
    Start([開始]) --> ModeMap[根據檢視模式確定可見 ISB 數量 n]
    ModeMap --> LoopISB[遍歷 ISB1 至 ISB8]
    LoopISB --> Boundary{索引 i > n?}
    Boundary -- 是 --> EndLoop
    Boundary -- 否 --> CheckFull{索引 i = n 且已載入?}
    CheckFull -- 是 --> ClearLast[呼叫 clearView 清空最後位置並傳回 ISB1]
    CheckFull -- 否 --> CheckEmpty{ISB 檔名是否為空?}
    CheckEmpty -- 是 --> ReturnISB[返回此 ISB 對象] --> End([結束])
    CheckEmpty -- 否 --> NextISB[處理下一個索引]
    NextISB --> LoopISB
    EndLoop --> End
    ClearLast --> End
```
reassemble/view/mermaid/GoViewMode.md
比對新檔案
@@ -0,0 +1,14 @@
```mermaid
flowchart TD
    Start([開始]) --> MatchVMode{判斷 VMode 索引}
    MatchVMode -- 0 (單頁) --> CallDisp0[呼叫 DisplayMode 1x1 佈局]
    MatchVMode -- 1 (兩頁) --> CallDisp1[呼叫 DisplayMode 1x1 佈局]
    MatchVMode -- 2 (網格) --> CallDisp2[呼叫 DisplayMode 2x2 佈局]
    MatchVMode -- 3 (自定義) --> CallDisp3[呼叫 DisplayMode 2x3 佈局]
    MatchVMode -- 4 (擴展) --> CallDisp4[呼叫 DisplayMode 2x4 佈局]
    CallDisp0 --> End([結束])
    CallDisp1 --> End
    CallDisp2 --> End
    CallDisp3 --> End
    CallDisp4 --> End
```
reassemble/view/mermaid/ISB1Click.md
比對新檔案
@@ -0,0 +1,10 @@
```mermaid
flowchart TD
    Start([開始]) --> SetISB[將 Sender 設為當前 DisplayISB]
    SetISB --> MoveShape[將 Shape1 選取框移動至該 ISB 位置]
    MoveShape --> CalcPage[依據 ISB 名稱與捲軸位置計算頁碼 p]
    CalcPage --> UpdatePageLV{p 是否在有效範圍內?}
    UpdatePageLV -- 是 --> SyncUI[更新 NowPage 與 PageLV 的選取索引]
    UpdatePageLV -- 否 --> End([結束])
    SyncUI --> End
```
reassemble/view/mermaid/ISB1ImageMouseDown.md
比對新檔案
@@ -0,0 +1,17 @@
```mermaid
flowchart TD
    Start([開始]) --> SetISB[設定當前 DisplayISB 並取得焦點]
    SetISB --> SyncUI[更新 Shape1 選取框與 PageLV 選取頁面]
    SyncUI --> ModeCheck{判斷當前模式}
    ModeCheck -- 拖曳 (NowClick = -1) --> CheckLeft{是否按下左鍵且有圖?}
    CheckLeft -- 是 --> StartDrag[啟動影像拖曳 BeginDrag]
    CheckLeft -- 否 --> RotateCheck
    ModeCheck -- 其他 --> RotateCheck{是否為旋轉模式?}
    RotateCheck -- 是 --> LoadImg[重新載入影像檔案]
    RotateCheck -- 否 --> End([結束])
    StartDrag --> End
    LoadImg --> End
```
reassemble/view/mermaid/ISB1ImageMouseUp.md
比對新檔案
@@ -0,0 +1,13 @@
```mermaid
flowchart TD
    Start([開始]) --> MouseMode{判斷滑鼠模式}
    MouseMode -- 刪除 (mmDelete) --> CallDel[觸發右鍵選單刪除功能 PM508Click]
    MouseMode -- 旋轉 (mmR90/180/270) --> SaveRotated[儲存旋轉後的檔案, 針對 JPG 設定品質]
    MouseMode -- 縮放/拖曳 (mmZoom/mmDrag) --> SaveScroll[呼叫 SetScrollData 記錄捲軸位置]
    SaveRotated --> SyncPreview[同步更新預覽圖 SelectISB 並清空檢核記錄]
    SyncPreview --> SaveScroll
    CallDel --> End([結束])
    SaveScroll --> End
```
reassemble/view/mermaid/Initialize.md
比對新檔案
@@ -0,0 +1,9 @@
```mermaid
flowchart TD
    Start([開始]) --> InheritInit[呼叫繼承的 Initialize]
    InheritInit --> RegEvents[註冊視窗與滑鼠事件處理函式]
    RegEvents --> SetDefaults[設定預設參數: MpsKey, Seg, Ext, SafePixel]
    SetDefaults --> SetLengths[設定案件與表單編號長度限制]
    SetLengths --> SetOther[設定去直線容忍值與切圖條碼類型]
    SetOther --> End([結束])
```
reassemble/view/mermaid/NewScanBtnClick.md
比對新檔案
@@ -0,0 +1,13 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckInit{初始化是否完成?}
    CheckInit -- 否 --> ShowWait[提示稍候] --> End([結束])
    CheckInit -- 是 --> SpecialMode{檢查特殊模式: RSCAN/ESCAN/DSCAN?}
    SpecialMode -- 是 --> AutoSelect[自動選取新件節點並觸發追加掃描]
    SpecialMode -- 否 --> NewScan[全新掃描: 重置模式與路徑, 清空清單]
    AutoSelect --> End
    NewScan --> CallScan[呼叫 StatrTwainScan 啟動掃描]
    CallScan --> Reload[掃描結束, 重新載入影像檔案] --> End
```
reassemble/view/mermaid/NextPageBtnClick.md
比對新檔案
@@ -0,0 +1,12 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckSelect{是否有選取影像?}
    CheckSelect -- 否 --> End([結束])
    CheckSelect -- 是 --> CallNext[呼叫 NextPage 跳轉]
    CallNext --> CheckBound{新影像是否超出可見高度?}
    CheckBound -- 是 --> AdjustScroll[自動調整垂直捲軸位置]
    CheckBound -- 否 --> End
    AdjustScroll --> End
```
reassemble/view/mermaid/OptionBtnClick.md
比對新檔案
@@ -0,0 +1,18 @@
```mermaid
flowchart TD
    Start([開始]) --> ShowLoading[顯示載入中提示]
    ShowLoading --> CreateDlg[建立 TPatchDlg 對話框]
    CreateDlg --> InitI18n[載入多國語言設定]
    InitI18n --> SyncParams[將系統參數同步至對話框控制項]
    SyncParams --> ShowDlg[顯示對話框供使用者編輯]
    ShowDlg -- 使用者按確定 --> SaveParams[將新參數存回系統變數]
    ShowDlg -- 使用者按取消 --> EndProc[結束處理]
    SaveParams --> WriteIni[呼叫 R_W_ScanIni 寫入設定檔]
    WriteIni --> UpdateUI[同步更新 UI 勾選狀態]
    UpdateUI --> EndProc
    EndProc --> FreeDlg[釋放對話框資源]
    FreeDlg --> HideLoading[停止載入提示] --> End([結束])
```
reassemble/view/mermaid/PM101Click.md
比對新檔案
@@ -0,0 +1,14 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckLevel{判斷選取節點層級}
    CheckLevel -- 根節點/全刪 --> DelAll[刪除所有案件實體目錄並重載]
    CheckLevel -- 案件層 --> DelCase[刪除案件目錄, 更新案件清單與索引]
    CheckLevel -- 文件層 --> DelDoc[判斷模式, 呼叫 DeleteDocNoFileForESCAN 或刪除目錄]
    CheckLevel -- 表單層 --> DelForm[呼叫 DeleteFormCodeFile 刪除表單影像]
    DelAll --> End([結束])
    DelCase --> ClearErr[清空檢核記錄] --> End
    DelDoc --> ClearErr
    DelForm --> UpdateUI[更新樹狀頁數統計與刷新] --> End
```
reassemble/view/mermaid/PM102Click.md
比對新檔案
@@ -0,0 +1,14 @@
```mermaid
flowchart TD
    Start([開始]) --> AskNewID[彈出輸入盒要求輸入新編號]
    AskNewID --> ValidCheck{驗證長度與是否重複?}
    ValidCheck -- 失敗 --> ShowErr[顯示錯誤訊息] --> End([結束])
    ValidCheck -- 成功 --> Confirm[詢問是否確認修改]
    Confirm -- 是 --> ExecRename[清空顯示, 執行磁碟目錄更名]
    Confirm -- 否 --> End
    ExecRename --> UpdateList[更新案件清單文字]
    UpdateList --> Redraw[重新繪製樹狀結構並提示完成] --> End
```
reassemble/view/mermaid/PM104Click.md
比對新檔案
@@ -0,0 +1,23 @@
```mermaid
flowchart TD
    Start([開始]) --> OpenDlg[開啟檔案對話框選取影像]
    OpenDlg -- 執行 --> SizeCheck{檔案大小是否超過限制?}
    SizeCheck -- 是 --> ShowErr[顯示超過限制訊息] --> End([結束])
    SizeCheck -- 否 --> ShowLoading[顯示載入中提示]
    ShowLoading --> LoopPages[逐頁載入影像並執行去偏斜]
    LoopPages --> BarcodeProc[條碼辨識判斷 FormID 與旋轉]
    BarcodeProc --> CropCheck{是否需 A3 切圖?}
    CropCheck -- 是 --> PerformCrop[左右分割影像]
    CropCheck -- 否 --> SingleImg[處理單張影像]
    PerformCrop --> SaveProc[轉換格式: 黑白轉 TIF, 彩色轉 JPG 並存檔]
    SingleImg --> SaveProc
    SaveProc --> UpdateCtx[更新索引與樹狀結構頁數統計]
    UpdateCtx --> NextPage[處理下一頁]
    NextPage --> LoopPages
    LoopPages -- 結束 --> ClearLog[清空檢核記錄並重載顯示] --> End
```
reassemble/view/mermaid/PM106Click.md
比對新檔案
@@ -0,0 +1,28 @@
```mermaid
flowchart TD
    Start([開始]) --> ShowLoading[顯示載入中提示]
    ShowLoading --> CreateForm[建立 TDocCopyForm 對話框]
    CreateForm --> InitI18n[載入多國語言與來源案件編號]
    InitI18n --> LoadDocs[從樹狀結構載入可複製的文件清單]
    LoadDocs --> LoadCases[從樹狀結構載入目標案件清單]
    LoadCases --> ShowDlg[顯示對話框]
    ShowDlg -- mrOk (確認) --> AskConfirm[詢問是否確定執行複製]
    ShowDlg -- 取消 --> EndProc[結束處理]
    AskConfirm -- 是 --> LoopTargets[遍歷選取的目標案件]
    LoopTargets --> LoopDocs[遍歷選取的來源文件]
    LoopDocs --> ExecCopy[執行 CopyFile 並更新目標案件 Context.dat]
    ExecCopy --> ClearErr[清空目標案件檢核記錄]
    ClearErr --> NextDoc[下一個文件]
    NextDoc --> LoopDocs
    LoopDocs -- 結束 --> NextCase[下一個案件]
    NextCase --> LoopTargets
    LoopTargets -- 結束 --> Reload[重新載入影像並提示完成]
    Reload --> EndProc
    EndProc --> FreeRes[釋放資源並停止提示] --> End([結束])
```
reassemble/view/mermaid/PM108Click.md
比對新檔案
@@ -0,0 +1,25 @@
```mermaid
flowchart TD
    Start([開始]) --> CreateDlg[建立 TDocListForm]
    CreateDlg --> LoadForms[從 FORM_INF_List 載入可用表單]
    LoadForms --> ShowDlg[顯示對話框]
    ShowDlg -- mrOk (確認) --> CheckCustom{是否歸類至自定義?}
    ShowDlg -- 取消 --> EndProc[結束處理]
    CheckCustom -- 是 --> NewFormID[取得自定義名稱與編號]
    CheckCustom -- 否 --> ExistFormID[取得選取的既有 FormID]
    NewFormID --> AskConfirm[詢問是否將所有影像歸類]
    ExistFormID --> AskConfirm
    AskConfirm -- 是 --> ExecReplace[呼叫 FormIDReplace 搬移影像檔案]
    AskConfirm -- 否 --> EndProc
    ExecReplace --> UpdateTree[更新索引與樹狀結構顯示]
    UpdateTree --> ClearLog[清空檢核記錄]
    ClearLog --> RestoreFocus[焦點回到案件節點並刷新]
    RestoreFocus --> EndProc
    EndProc --> FreeRes[釋放資源] --> End([結束])
```
reassemble/view/mermaid/PM109Click.md
比對新檔案
@@ -0,0 +1,14 @@
```mermaid
flowchart TD
    Start([開始]) --> ClearView[清空目前影像顯示]
    ClearView --> ShowLoading[顯示 OMR 檢核中提示]
    ShowLoading --> CallCheck[呼叫 OMRCheckCase 執行案件檢核]
    CallCheck --> CheckOk{檢核是否成功?}
    CheckOk -- 是 --> CreateMark[建立 OMRCheckOk.dat 標記檔]
    CheckOk -- 否 --> EndProc[結束處理]
    CreateMark --> EndProc
    EndProc --> Reload[重載影像並刷新樹狀結構]
    Reload --> ShowMsg[提示檢核完成] --> End([結束])
```
reassemble/view/mermaid/PM110Click.md
比對新檔案
@@ -0,0 +1,12 @@
```mermaid
flowchart TD
    Start([開始]) --> AskName[要求輸入其他文件名稱]
    AskName -- 確認 --> CheckDup{名稱是否已存在?}
    AskName -- 取消 --> End([結束])
    CheckDup -- 是 --> ShowErr[提示名稱已存在] --> End
    CheckDup -- 否 --> GenID[產生新文件編號與建立子目錄]
    GenID --> SyncList[將新目錄加入清單並繪製樹狀結構]
    SyncList --> AutoExpand[自動展開新建立的節點] --> End
```
reassemble/view/mermaid/PM111Click.md
比對新檔案
@@ -0,0 +1,17 @@
```mermaid
flowchart TD
    Start([開始]) --> GetOld[取得目前文件份數]
    GetOld --> AskNew[彈出輸入盒供使用者修改份數]
    AskNew --> ValidRange{輸入是否在 1-9999 範圍內?}
    ValidRange -- 否 --> ShowErr[顯示輸入錯誤] --> End([結束])
    ValidRange -- 是 --> CheckDiv{是否為分份文件且設為 1?}
    CheckDiv -- 是 --> AskConfirm[詢問是否確定修改並限制後續更動]
    CheckDiv -- 否 --> ExecUpdate[更新 SetDocDirCopies 並標記已編輯]
    AskConfirm -- 確認 --> ExecUpdate
    AskConfirm -- 取消 --> End
    ExecUpdate --> Redraw[刷新樹狀結構統計並提示完成] --> End
```
reassemble/view/mermaid/PM401Click.md
比對新檔案
@@ -0,0 +1,14 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckIndex{選取索引是否為第一頁?}
    CheckIndex -- 是 --> ShowErr[提示不能從第一頁分案] --> End([結束])
    CheckIndex -- 否 --> AskConfirm[詢問是否確定分案]
    AskConfirm -- 否 --> End
    AskConfirm -- 是 --> Prepare[清空檢核記錄並取得新流水案號]
    Prepare --> LoopMove[搬移指定頁碼後的所有影像至新目錄]
    LoopMove --> UpdateDat[同步更新原案與新案的 Context.dat]
    UpdateDat --> SyncIndex[寫入案件索引 CaseIndex.dat]
    SyncIndex --> Reload[重新載入影像列表並提示完成] --> End
```
reassemble/view/mermaid/PM404Click.md
比對新檔案
@@ -0,0 +1,17 @@
```mermaid
flowchart TD
    Start([開始]) --> ShowLoading[顯示載入中提示]
    ShowLoading --> CreateDlg[建立 TDocListForm 表單清單]
    CreateDlg --> LoadForms[從 FORM_INF_List 載入可用表單]
    LoadForms --> ShowDlg[顯示對話框]
    ShowDlg -- mrOk (確認) --> GetFormID[獲取選取的 FormID]
    ShowDlg -- 取消 --> EndProc[結束處理]
    GetFormID --> Reclassify[依 TreeView 層級呼叫 PageReplaceFormID]
    Reclassify --> Redraw[重新繪製樹狀結構並清空檢核記錄]
    Redraw --> RestoreFocus[回到原先選取的節點並點擊]
    RestoreFocus --> EndProc
    EndProc --> FreeRes[釋放資源並停止提示] --> End([結束])
```
reassemble/view/mermaid/PM505Click.md
比對新檔案
@@ -0,0 +1,10 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckFile{當前是否有載入影像?}
    CheckFile -- 否 --> End([結束])
    CheckFile -- 是 --> SetMode[設定掃描模式為 smReplace]
    SetMode --> SetPath[指定儲存路徑與檔名為當前顯示之影像]
    SetPath --> StartScan[呼叫 StatrTwainScan 執行掃描覆蓋]
    StartScan --> ClearLog[清空該案件的檢核記錄] --> End
```
reassemble/view/mermaid/PM507Click.md
比對新檔案
@@ -0,0 +1,17 @@
```mermaid
flowchart TD
    Start([開始]) --> ShowLoading[顯示載入中提示]
    ShowLoading --> CreateDlg[建立 TDocListForm]
    CreateDlg --> LoadForms[載入可用表單]
    LoadForms --> ShowDlg[顯示對話框]
    ShowDlg -- mrOk (確認) --> CalcName[依 FormID 產生新的序號檔名]
    ShowDlg -- 取消 --> EndProc[結束處理]
    CalcName --> ExecRename[執行 RenameFile 更名並更新 Context.dat]
    ExecRename --> Redraw[重新整理樹狀結構並清空檢核記錄]
    Redraw --> RestoreFocus[焦點回到原文件節點並點擊]
    RestoreFocus --> EndProc
    EndProc --> FreeRes[釋放資源] --> End([結束])
```
reassemble/view/mermaid/PM508Click.md
比對新檔案
@@ -0,0 +1,20 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckLast{是否為案件最後一張影像?}
    CheckLast -- 是 --> AskDelCase[詢問是否刪除整個案件]
    CheckLast -- 否 --> AskDelImg[詢問是否確定刪除影像]
    AskDelCase -- 是 --> DelTree[執行 _DelTree 刪除案件目錄]
    AskDelCase -- 否 --> End([結束])
    AskDelImg -- 是 --> ExecDel[從清單移除項目, 刪除實體檔案]
    AskDelImg -- 否 --> End
    ExecDel --> ReSort[呼叫 ReSortFileName 重新排序檔案]
    ReSort --> UpdateTree[更新樹狀結構統計與顯示]
    UpdateTree --> ClearLog[清空檢核記錄]
    DelTree --> Reload[重載影像檔案清單]
    ClearLog --> End
    Reload --> End
```
reassemble/view/mermaid/PM601Click.md
比對新檔案
@@ -0,0 +1,21 @@
```mermaid
flowchart TD
    Start([開始]) --> ShowLoading[顯示載入中提示]
    ShowLoading --> CreateDlg[建立 TDocListForm]
    CreateDlg --> LoopSelected[遍歷縮圖區選取的影像]
    LoopSelected --> GetTarget{選擇歸類目標}
    GetTarget -- 既有表單 --> CalcPath[判斷是否分份, 確定儲存目錄]
    GetTarget -- 自定義 --> NewDir[產生新文件編號與目錄]
    CalcPath --> FileOp[執行檔案複製與刪除原檔案]
    NewDir --> FileOp
    FileOp --> UpdateCtx[更新目標與原目錄的 ContextList]
    UpdateCtx --> NextImg[處理下一個選取影像]
    NextImg --> LoopSelected
    LoopSelected -- 結束 --> ReSort[執行檔案重新排序]
    ReSort --> Redraw[更新樹狀結構並清空檢核記錄]
    Redraw --> FreeRes[釋放資源並停止提示] --> End([結束])
```
reassemble/view/mermaid/PM602Click.md
比對新檔案
@@ -0,0 +1,20 @@
```mermaid
flowchart TD
    Start([開始]) --> AskName[彈出對話框要求輸入文件名稱]
    AskName -- 確認 --> CheckDup{名稱是否已存在?}
    AskName -- 取消 --> End([結束])
    CheckDup -- 是 --> ShowErr[提示名稱已存在] --> End
    CheckDup -- 否 --> GenID[產生新的自定義文件編號與目錄]
    GenID --> SyncList[將新目錄加入清單並更新編輯狀態]
    SyncList --> LoopSelected[遍歷選取的影像]
    LoopSelected --> ExecCopy[執行 CopyFile 與 SetContextList]
    ExecCopy --> ExecDel[呼叫 DeleteImageFile 刪除原檔案]
    ExecDel --> NextImg[下一個影像]
    NextImg --> LoopSelected
    LoopSelected -- 結束 --> ReSort[重新排序檔案並刷新顯示]
    ReSort --> UpdateTree[更新樹狀結構並清空檢核記錄] --> End
```
reassemble/view/mermaid/PM604Click.md
比對新檔案
@@ -0,0 +1,11 @@
```mermaid
flowchart TD
    Start([開始]) --> LoopComps[遍歷縮圖區選取的影像元件]
    LoopComps --> FindISB[尋找對應的 TImageScrollBox]
    FindISB --> ExecDeskew[執行 DeskewImg 自動去偏斜]
    ExecDeskew --> Redraw[重新繪製影像並存回原檔案]
    Redraw --> SyncMain[更新主顯示區影像]
    SyncMain --> NextComp[處理下一個元件]
    NextComp --> LoopComps
    LoopComps -- 結束 --> End([結束])
```
reassemble/view/mermaid/PM605Click.md
比對新檔案
@@ -0,0 +1,14 @@
```mermaid
flowchart TD
    Start([開始]) --> AskConfirm[詢問是否確定刪除?]
    AskConfirm -- 否 --> End([結束])
    AskConfirm -- 是 --> LoopSelected[遍歷所有由 Shape 標記的選取影像]
    LoopSelected --> CallDel[呼叫 DeleteImageFile 刪除實際檔案]
    CallDel --> NextImg[處理下一個]
    NextImg --> LoopSelected
    LoopSelected -- 結束 --> ReSort[執行檔案重新排序 ReSortFileName]
    ReSort --> UpdateTree[更新樹狀結構頁數統計與刷新顯示]
    UpdateTree --> ClearLog[清空案件檢核記錄] --> End
```
reassemble/view/mermaid/PopupMenu1Popup.md
比對新檔案
@@ -0,0 +1,19 @@
```mermaid
flowchart TD
    Start([開始]) --> HideAll[隱藏 PM101 至 PM111 所有項]
    HideAll --> CheckSelect{是否有選取節點?}
    CheckSelect -- 否 --> End([結束])
    CheckSelect -- 是 --> LevelCheck{判斷選取層級}
    LevelCheck -- 根節點 --> NewScanItems[顯示刪除, 掃描加入, 檔案加入]
    LevelCheck -- 案件層 --> CaseItems[顯示刪除, 掃描加入, 寫備註, 檢核, 檔案加入等]
    LevelCheck -- 文件層 --> DocItems[顯示刪除, 修改份數, 修改編號等]
    LevelCheck -- 表單層 --> FormItems[顯示刪除, 歸類, 掃描加入, 檔案加入等]
    CaseItems --> AuthCheck[根據 FImgDelete 與 CaseDelete_Enable 設定可用性]
    DocItems --> AuthCheck
    FormItems --> AuthCheck
    AuthCheck --> End
    NewScanItems --> End
```
reassemble/view/mermaid/PopupMenu6Popup.md
比對新檔案
@@ -0,0 +1,14 @@
```mermaid
flowchart TD
    Start([開始]) --> InitVisibility[設定預設可見性: 歸類, 自定義, 刪除為 True]
    InitVisibility --> CheckDoc{是否為附件目錄?}
    CheckDoc -- 是 --> ExtraItems[顯示歪斜矯正, 自定義等所有功能]
    CheckDoc -- 否 --> ModeCheck{是否為異動件模式?}
    ExtraItems --> AuthCheck[根據 FImgDelete 權限與 UseCase 引用狀態設定可用性]
    ModeCheck -- 是 --> HideItems[隱藏部分歸類功能] --> AuthCheck
    ModeCheck -- 否 --> AuthCheck
    AuthCheck --> End([結束])
```
reassemble/view/mermaid/SampleScanBtnClick.md
比對新檔案
@@ -0,0 +1,21 @@
```mermaid
flowchart TD
    Start([開始]) --> SetMode[設定掃描模式為範本模式 smSample]
    SetMode --> SetUI[切換單頁顯示並重置計數]
    SetUI --> InputFormID[引導使用者輸入 FormID]
    InputFormID --> CheckReg{FormID 是否已註冊?}
    CheckReg -- 否 --> ShowErr[顯示未註冊訊息] --> End([結束])
    CheckReg -- 是 --> StartScan[呼叫 StatrTwainScan 執行掃描]
    StartScan --> CheckFile{是否成功產生 TIF 影像?}
    CheckFile -- 否 --> End
    CheckFile -- 是 --> GenJPG[產生 JPG 預覽圖]
    GenJPG --> UploadTIF[透過 HTTPS 上傳 TIF 範本]
    UploadTIF --> CheckSess{Session 是否有效?}
    CheckSess -- 否 --> ShowLogin[提示重新登入] --> End
    CheckSess -- 是 --> UploadJPG[上傳 JPG 影像]
    UploadJPG --> Success[提示傳送完成] --> End
```
reassemble/view/mermaid/TransBtnClick.md
比對新檔案
@@ -0,0 +1,26 @@
```mermaid
flowchart TD
    Start([開始]) --> CheckPre[完整前置檢查: 配號, 歸類, 時間限制]
    CheckPre --> AllPass{檢查是否通過?}
    AllPass -- 否 --> End([結束])
    AllPass -- 是 --> ShowLoading[顯示上傳中提示]
    ShowLoading --> LoopCases[遍歷所有案件]
    LoopCases --> Prepare[產生入庫文件檔與影像結構]
    Prepare --> AskServer{詢問伺服器是否可上傳?}
    AskServer -- 否/重複 --> LogCount[更新統計並跳過]
    AskServer -- 是 --> ProcCase[執行 CaseReSize 與 OMR 檢核]
    ProcCase --> CheckOk{檢核是否通過?}
    CheckOk -- 否 --> LogErr[記錄檢核失敗案件]
    CheckOk -- 是 --> CallTrans[呼叫 TransCaseID 執行封裝傳送]
    CallTrans --> NextCase[處理下一個案件]
    LogCount --> NextCase
    LogErr --> NextCase
    NextCase --> LoopCases
    LoopCases -- 結束 --> ShowReport[顯示成功/失敗統計報告]
    ShowReport --> Reload[重新載入影像並結束] --> End
```
reassemble/view/mermaid/ViewMouseMode.md
比對新檔案
@@ -0,0 +1,9 @@
```mermaid
flowchart TD
    Start([開始]) --> MapMode[依參數 v 對應至 mmAmplifier, mmZoom, mmDrag, mmDelete 等]
    MapMode --> LoopISB[遍歷所有影像捲軸盒 ISB1-ISB8]
    LoopISB --> SetMouse[將對應的 MouseMode 指派給元件]
    SetMouse --> NextISB[處理下一個]
    NextISB --> LoopISB
    LoopISB -- 結束 --> End([結束])
```
reassemble/view/mermaid/WNoteBtnClick.md
比對新檔案
@@ -0,0 +1,17 @@
```mermaid
flowchart TD
    Start([開始]) --> ShowLoading[顯示載入中提示]
    ShowLoading --> CreateForm[建立 TSortMemoForm 編輯視窗]
    CreateForm --> InitData[從 MEMO_INF_List 載入註記範本]
    InitData --> LoadExisting[讀取既有 Scan_Memo.dat 內容並顯示]
    LoadExisting --> ShowDlg[顯示編輯視窗]
    ShowDlg -- mrOk (確認) --> SaveData[將編輯結果存回 Scan_Memo.dat]
    ShowDlg -- 取消 --> FreeRes[釋放資源]
    SaveData --> FreeRes
    FreeRes --> HideLoading[停止載入提示]
    HideLoading --> CheckRefresh{是否需要更新狀態?}
    CheckRefresh -- 是 --> UpdateCase[觸發 CaseHelpBtnClick] --> End([結束])
    CheckRefresh -- 否 --> End
```
reassemble/view/misc.pas
@@ -867,7 +867,7 @@
{ ==============================================================================
  方法名稱:AddAttFileLBClick
  引用相依:FileExists, CopyFile
  引用相依:CopyFile, FileExists
  方法描述:處理「加入附加電子檔」按鈕點擊。開啟檔案對話框選取多個 PDF 檔案,支援覆
            蓋檢查。執行 CopyFile 將檔案複製到案件目錄下,並呼叫 SetAttContextList
             更新附加檔案清單後載入顯示。
@@ -1044,7 +1044,7 @@
{ ==============================================================================
  方法名稱:Button5Click
  引用相依:SetFtpInfo, IIS_Ftp
  引用相依:IIS_Ftp, SetFtpInfo
  方法描述:測試 FTP 上傳功能。連線 FTP 後嘗試將特定的 PDF 檔案上傳至伺服器路徑。
============================================================================== }
procedure TCB_IMGPSScanX.Button5Click(Sender: TObject);
@@ -1059,7 +1059,7 @@
{ ==============================================================================
  方法名稱:Button6Click
  引用相依:SetFtpInfo, IIS_Ftp, Rotate, FJpgCompression, Scanner
  引用相依:FJpgCompression, IIS_Ftp, Rotate, Scanner, SetFtpInfo
  方法描述:測試 FTP 下載功能。連線 FTP 後嘗試從伺服器下載 ZIP 案件檔至本地。
============================================================================== }
procedure TCB_IMGPSScanX.Button6Click(Sender: TObject);
@@ -1076,7 +1076,7 @@
{ ==============================================================================
  方法名稱:ExportBtClick
  引用相依:SaveToFile, FileExists, En_DecryptionStr_Base64, dnFile, dnFile_Get
  引用相依:En_DecryptionStr_Base64, FileExists, SaveToFile, dnFile, dnFile_Get
  方法描述:處理「匯出授權檔」按鈕點擊。透過 HTTPS 下載掃瞄與檢視用的 .lic 授權檔案
            。將授權檔連同加密的 mps.dat 檔案打包成帶有密碼保護的 mps.zip 壓縮包,
            完成後清理暫存檔並提示路徑。
@@ -1147,8 +1147,8 @@
{ ==============================================================================
  方法名稱:ImportBtClick
  引用相依:Str2Dir, FileExists, LoadFromFile, RenameFile, _DelTree, En_Decrypti
            onStr_Base64, upFile
  引用相依:En_DecryptionStr_Base64, FileExists, LoadFromFile, RenameFile, Str2D
            ir, _DelTree, upFile
  方法描述:處理「匯入授權檔」按鈕點擊。選取 mps.zip 授權包後進行解壓與過期驗證。驗
            證通過後對授權檔執行重新命名,並透過 upFile 函式逐一上傳至伺服器範本
            目錄,過程中會嚴格檢查 Session 與回傳狀態。
@@ -1423,7 +1423,7 @@
{ ==============================================================================
  方法名稱:CaseReSize
  引用相依:ImageReSize_FormID, FileExists, LoadFromFile
  引用相依:FileExists, ImageReSize_FormID, ImageResize, LoadFromFile
  方法描述:對案件執行影像縮放處理。清空舊有的檢核與定位錯誤記錄,隨後遍歷影像清單
            ,對每個檔案執行 ImageReSize_FormID 處理。
============================================================================== }
@@ -1453,8 +1453,8 @@
{ ==============================================================================
  方法名稱:TransCaseID
  引用相依:FileExists, LoadFromFile, CopyFile, FindFirst, _DelTree, upFile, Set
            FtpInfo, IIS_Ftp, FtpCaseComplete
  引用相依:CopyFile, FileExists, FindFirst, FtpCaseComplete, IIS_Ftp, LoadFromF
            ile, SetFtpInfo, _DelTree, upFile
  方法描述:傳送案件核心程序。包含排序影像、產生描述檔(FormID/DocNo對照、OMR資訊、附
            件狀態等)、建立 ZIP 壓縮包(含主圖與遮罩)並檢查大小。最後根據 HTTP 或 F
            TP 模式上傳至伺服器。上傳完成後針對異動模式處理舊件引入,並在最後清理
@@ -1823,8 +1823,8 @@
{ ==============================================================================
  方法名稱:GetNode2Name
  引用相依:FileExists, Str2Dir, SaveToFile, LoadFromFile, DeleteDocNoFile, Copy
            File, DirectoryExists, En_DecryptionStr_Base64, dnFile
  引用相依:CopyFile, DeleteDocNoFile, DirectoryExists, En_DecryptionStr_Base64,
             FileExists, LoadFromFile, SaveToFile, Str2Dir, dnFile
  方法描述:提取文件層級節點的識別名稱字串,用於記錄與恢復節點選取狀態。
============================================================================== }
Function TCB_IMGPSScanX.GetNode2Name(Node2:TTreeNode):String;  //取MyTreeNode2的識別字出來(記之前點選用)
@@ -1949,7 +1949,7 @@
{ ==============================================================================
  方法名稱:DownLoadImage
  引用相依:SetFtpInfo, IIS_Ftp
  引用相依:IIS_Ftp, SetFtpInfo
  方法描述:處理影像下載流程。根據案件上傳/下載方式(HTTP 或 FTP),從伺服器下載對應
            的 ZIP 檔案並解壓縮至本地案件目錄,供後續異動或補件使用。
============================================================================== }
@@ -2002,7 +2002,7 @@
{ ==============================================================================
  方法名稱:Down_Img
  引用相依:FileExists, Str2Dir, En_DecryptionStr_Base64, dnFile_Get, dnFile
  引用相依:En_DecryptionStr_Base64, FileExists, Str2Dir, dnFile, dnFile_Get
  方法描述:透過 HTTPS 從伺服器下載案件影像。下載 ZIP 檔案(含 img.zip 與 att.zip)
            後執行本地解壓縮,將主影像與附件部署至指定目錄。
============================================================================== }
@@ -2062,7 +2062,7 @@
{ ==============================================================================
  方法名稱:GetNoNameCase
  引用相依:GetNoNameCase, DirectoryExists
  引用相依:DirectoryExists, GetNoNameCase
  方法描述:在指定的本地路徑中尋找尚未被佔用的「未配號XXXX」目錄名稱。
============================================================================== }
Function TCB_IMGPSScanX.GetNoNameCase(Path:String):String; //取未配號XXXX
@@ -2082,7 +2082,7 @@
{ ==============================================================================
  方法名稱:CaseResort
  引用相依:LoadFromFile, FileExists, RenameFile, SaveToFile, ReSortFileName
  引用相依:FileExists, LoadFromFile, ReSortFileName, RenameFile, SaveToFile
  方法描述:對案件檔案進行實體重新排序。依據文件清單(Doc_Inf_List)的順序,對主文件
            與次文件進行更名與重新編號,確保檔名序號符合業務邏輯。
============================================================================== }
@@ -2227,7 +2227,7 @@
{ ==============================================================================
  方法名稱:CaseResort2Scanlist
  引用相依:FileExists, LoadFromFile, RenameFile, SaveToFile, ReSortFileName
  引用相依:FileExists, LoadFromFile, ReSortFileName, RenameFile, SaveToFile
  方法描述:產生依表單代號排序的影像清單(scanlist.dat),用於上傳。
============================================================================== }
Procedure TCB_IMGPSScanX.CaseResort2Scanlist(Path:String); //案件的檔案重新排序給scanlist(次文件依FormID排)
@@ -2355,7 +2355,7 @@
{ ==============================================================================
  方法名稱:DistinctDocinCase
  引用相依:LoadFromFile, LoadFileGetMD5
  引用相依:LoadFileGetMD5, LoadFromFile
  方法描述:列出案件目錄下所有具備文件編號與版本的唯一組合。
============================================================================== }
Procedure TCB_IMGPSScanX.DistinctDocinCase(Path:String); //列出案件裡的Docno_版本
@@ -2843,7 +2843,7 @@
{ ==============================================================================
  方法名稱:FormIDReplace
  引用相依:FileExists, LoadFromFile, DirectoryExists, CopyFile, RenameFile, Sav
  引用相依:CopyFile, DirectoryExists, FileExists, LoadFromFile, RenameFile, Sav
            eToFile
  方法描述:將指定文件中的舊表單代碼替換為新代碼。首先決定目標目錄(考慮是否分份數
            、補件狀況),若目標目錄不存在則建立。接著將符合舊代碼的檔案複製到新目錄
@@ -3187,7 +3187,7 @@
{ ==============================================================================
  方法名稱:WriteResize
  引用相依:LoadFromFile, FileExists, SaveToFile
  引用相依:FileExists, GetTag, LoadFromFile, SaveToFile
  方法描述:產生影像縮放記錄檔(Resize.dat)。載入影像後比對原始標記(Tag)中的長寬資
            訊與實際 Graphic 的長寬,若有變動則將差異記錄至文字檔中。
============================================================================== }
@@ -3719,7 +3719,7 @@
{ ==============================================================================
  方法名稱:ScanGrayCBClick
  引用相依:ifGray256, ifBlackWhite, ifTrueColor
  引用相依:ifBlackWhite, ifGray256, ifTrueColor
  方法描述:根據掃瞄勾選框狀態,設定掃瞄色彩模式(灰階、全彩或黑白)。
============================================================================== }
procedure TCB_IMGPSScanX.ScanGrayCBClick(Sender: TObject);
@@ -4180,7 +4180,7 @@
{ ==============================================================================
  方法名稱:UseOldCaseLbClick
  引用相依:FileExists, LoadFromFile, DirectoryExists, CopyFile, SaveToFile
  引用相依:CopyFile, DirectoryExists, FileExists, LoadFromFile, SaveToFile
  方法描述:處理「使用舊件」功能。開啟 TOldCaseInfoForm 讓使用者選擇舊有案件的文件。
            選定後,將舊件影像複製到新案件目錄下,自動產生新序號檔名,建立關聯記錄(
            UseCase.dat),並同步更新新案件的文件清單與樹狀統計。
@@ -4689,7 +4689,7 @@
{ ==============================================================================
  方法名稱:CheckAvailable
  引用相依:FileExists, dnFile_Get, dnFile, upFile
  引用相依:FileExists, dnFile, dnFile_Get, upFile
  方法描述:檢查元件的使用授權。透過 HTTPS 下載掃瞄授權檔,並驗證 MacID、註冊數量與
            使用期限。若尚未註冊且仍有額度,則自動進行註冊並上傳新的授權檔至伺服器
            。最後在狀態列顯示註冊資訊。
@@ -4811,8 +4811,8 @@
{ ==============================================================================
  方法名稱:Case2Mask
  引用相依:FindPoint, DirectoryExists, _DelTree, Str2Dir, LoadFromFile, FileExi
            sts
  引用相依:DirectoryExists, FileExists, FindPoint, LoadFromFile, Str2Dir, _DelT
            ree
  方法描述:產生案件的遮罩影像(用於遮蔽敏感個資)。讀取 Context.dat,針對每張影像尋
            找定位點,並依據對應表單的 XML 定義執行區域遮罩,最後存入指定目錄。
============================================================================== }
@@ -5287,7 +5287,7 @@
{ ==============================================================================
  方法名稱:MoveImage
  引用相依:LoadFromFile, RenameFile, SaveToFile, ReSortFileName
  引用相依:LoadFromFile, ReSortFileName, RenameFile, SaveToFile
  方法描述:執行影像頁面的位置移動。先對目錄下所有檔案進行臨時更名(加上 @ 標記),
            根據選取狀態重新排列清單順序,最後更新 Context.dat 並重新排序實體檔案
            。
@@ -5353,7 +5353,7 @@
{ ==============================================================================
  方法名稱:MoveImage_Drag
  引用相依:LoadFromFile, RenameFile, SaveToFile, ReSortFileName
  引用相依:LoadFromFile, ReSortFileName, RenameFile, SaveToFile
  方法描述:處理影像拖拉移動。邏輯與 MoveImage 相似,但針對單一來源索引移動至目標
            索引的情境進行排列。
============================================================================== }
@@ -5579,7 +5579,7 @@
{ ==============================================================================
  方法名稱:view_image_DocNo
  引用相依:DpiResize, FileExists, LoadFromFile, DirectoryExists
  引用相依:DirectoryExists, DpiResize, FileExists, LoadFromFile
  方法描述:根據文件代號(DocNo)或表單代號(FormID)顯示影像。函式包含三種模式:顯示
            案件內所有影像(ShowAll)、顯示指定文件夾(如 Attach)下的影像,以及顯示指
            定文件代號下特定表單的影像。處理過程中會檢查在席狀態(In_WH)、執行影像 
reassemble/view/popupMenu.pas
@@ -1,7 +1,7 @@
{ ==============================================================================
  方法名稱:PM401Click
  引用相依:GetNoNameCase, Str2Dir, RenameFile, SaveToFile, FileExists, LoadFrom
            File
  引用相依:FileExists, GetNoNameCase, LoadFromFile, RenameFile, SaveToFile, Str
            2Dir
  方法描述:影像列表右鍵選單功能:從指定頁面分出新案。確認使用者選取的分案起點(不
            能為第一頁)後,取得新的流水案號並建立目錄。將原案件中該頁碼之後的所有
            影像檔案更名並搬移至新案目錄,同步更新原案與新案的 Context.dat 與 Cas
@@ -174,8 +174,8 @@
{ ==============================================================================
  方法名稱:PM601Click
  引用相依:FileExists, LoadFromFile, DirectoryExists, CopyFile, SaveToFile, Del
            eteImageFile, RenameFile, ReSortFileName
  引用相依:CopyFile, DeleteImageFile, DirectoryExists, FileExists, LoadFromFile
            , ReSortFileName, RenameFile, SaveToFile
  方法描述:縮圖瀏覽區右鍵選單功能:文件歸類。針對所有被選取(由 Shape 標記)的影像,
            開啟 TDocListForm 選擇目標表單。核心邏輯包含:判斷目標文件是否需要區分
            份數、自動產生新的文件目錄或沿用既有目錄、根據檔案序號產生新檔名、執行
@@ -339,8 +339,8 @@
{ ==============================================================================
  方法名稱:PM602Click
  引用相依:Str2Dir, FileExists, LoadFromFile, CopyFile, SaveToFile, DeleteImage
            File, ReSortFileName
  引用相依:CopyFile, DeleteImageFile, FileExists, LoadFromFile, ReSortFileName,
             SaveToFile, Str2Dir
  方法描述:縮圖瀏覽區右鍵選單功能:歸類至自定義文件。彈出對話框要求使用者輸入新文
            件名稱,檢核名稱是否重複後產生新的自定義文件編號。接著將所有選取的影像
            複製到新建立的文件目錄下,更新 ContextList並刪除原檔案。最後重新排序並
@@ -428,7 +428,7 @@
{ ==============================================================================
  方法名稱:PM604Click
  引用相依:DeskewImg, SaveToFile, LoadFromFile
  引用相依:DeskewImg, LoadFromFile, SaveToFile
  方法描述:縮圖瀏覽區右鍵選單功能:自動去偏斜(Deskew)。遍歷所有選取的影像元件,對
            其 Graphic 執行 DeskewImg 操作,重新繪製並將結果存回原檔案。
============================================================================== }
@@ -504,7 +504,7 @@
{ ==============================================================================
  方法名稱:PM101Click
  引用相依:_DelTree, DirectoryExists, DeleteDocNoFile
  引用相依:DeleteDocNoFile, DirectoryExists, _DelTree
  方法描述:處理樹狀結構(TreeView)的右鍵刪除選單。根據選取的節點類型(新掃瞄件、案
            件、文件或表單)執行不同範圍的刪除:包含刪除實體目錄、清空影像清單、更新
            案件索引及檢核記錄。針對異動模式(ESCAN),若刪除後無影像則會重建空案件
@@ -713,11 +713,11 @@
{ ==============================================================================
  方法名稱:PM104Click
  引用相依:TTiffGraphic, TJpegGraphic, FJpgCompression, DeskewImg, ConvertToBW,
             MpsGetBarcode, Rotate, CheckNeedCrop, CropImg, ifBlackWhite, tcGrou
            p4, ifColor25, ConvertToGray, tcJpeg, ifTrueColor, ifGray256, SaveQu
            ality, FindFirst, LoadFromFile, SaveToFile, DirectoryExists, _DelTre
            e, GetNoNameCase, Str2Dir, FileExists
  引用相依:CheckNeedCrop, ConvertToBW, ConvertToGray, CropImg, DeskewImg, Direc
            toryExists, FJpgCompression, FileExists, FindFirst, GetNoNameCase, L
            oadFromFile, MpsGetBarcode, Rotate, SaveQuality, SaveToFile, Str2Dir
            , TJpegGraphic, TTiffGraphic, _DelTree, ifBlackWhite, ifColor25, ifG
            ray256, ifTrueColor, tcGroup4, tcJpeg
  方法描述:處理「匯入影像檔案」右鍵選單。開啟檔案對話框選取 TIF/JPG/PNG 檔,並檢查
            檔案大小是否超過限制。核心流程包含:計算總頁數、逐頁載入、執行自動去偏斜
            (Deskew)、條碼辨識以判斷 FormID、處理 A3 切圖(左右分割)、將影像轉換為對
@@ -1660,7 +1660,7 @@
{ ==============================================================================
  方法名稱:PM508Click
  引用相依:_DelTree, SaveToFile, ReSortFileName, LoadFromFile, FileExists
  引用相依:FileExists, LoadFromFile, ReSortFileName, SaveToFile, _DelTree
  方法描述:處理影像區域的「刪除影像」右鍵選單。若案件僅剩一張影像則詢問是否刪除整
            個案件目錄。否則,在確認後從影像清單中移除該項目、刪除實體檔案並呼叫 Re
            SortFileName 重新排序。最後刷新樹狀統計文字、清空檢核記錄並更新顯示。
reassemble/view/scrollView.pas
@@ -141,7 +141,7 @@
{ ==============================================================================
  方法名稱:ISB1ImageMouseUp
  引用相依:TJpegGraphic, SaveQuality, SaveToFile
  引用相依:SaveQuality, SaveToFile, TJpegGraphic
  方法描述:影像滑鼠放開事件。處理多種滑鼠模式:在 mmDelete 模式下觸發刪除功能;在
            旋轉模式(mmR90等)下儲存旋轉後的影像檔案,並同步更新預覽圖(SelectISB)
            與清空檢核記錄。最後針對縮放或拖曳模式更新捲軸位置數據。
@@ -229,7 +229,7 @@
{ ==============================================================================
  方法名稱:ImageScrollBox1NewGraphic
  引用相依:TDibGraphic, ConvertToBW
  引用相依:ConvertToBW, TDibGraphic
  方法描述:當載入新影像時觸發。將當前 Graphic 內容同步至黑白影像處理元件(ISB_BW)
            ,若原始影像非黑白格式則執行 ConvertToBW 轉換。
============================================================================== }
reassemble/view/toolBar.pas
@@ -545,7 +545,7 @@
{ ==============================================================================
  方法名稱:SelectScanBtnClick
  引用相依:SelectScanner, scanner.SelectScanner, Scanner
  引用相依:Scanner, SelectScanner, scanner.SelectScanner
  方法描述:開啟系統掃瞄器選擇視窗。
============================================================================== }
procedure TCB_IMGPSScanX.SelectScanBtnClick(Sender: TObject);
@@ -737,7 +737,7 @@
{ ==============================================================================
  方法名稱:SpeedButton14Click
  引用相依:Rotate, TJpegGraphic, SaveQuality, LoadFromFile, SaveToFile
  引用相依:LoadFromFile, Rotate, SaveQuality, SaveToFile, TJpegGraphic
  方法描述:處理工具列按鈕點擊,執行影像逆時針旋轉 90 度(即旋轉 270 度)。程序會重
            新載入影像、執行旋轉、根據影像格式(JPG 則設定品質為 30)儲存回原檔案,最
            後更新主顯示區與縮圖區,並重新調整縮圖佈局。
@@ -768,7 +768,7 @@
{ ==============================================================================
  方法名稱:SpeedButton15Click
  引用相依:Rotate, TJpegGraphic, SaveQuality, LoadFromFile, SaveToFile
  引用相依:LoadFromFile, Rotate, SaveQuality, SaveToFile, TJpegGraphic
  方法描述:處理工具列按鈕點擊,執行影像旋轉 180 度。程序重新載入檔案後執行旋轉操
            作,並依據格式存檔。完成後同步更新主畫面與預覽圖,確保顯示一致性。
============================================================================== }
@@ -796,7 +796,7 @@
{ ==============================================================================
  方法名稱:SpeedButton16Click
  引用相依:Rotate, TJpegGraphic, SaveQuality, LoadFromFile, SaveToFile
  引用相依:LoadFromFile, Rotate, SaveQuality, SaveToFile, TJpegGraphic
  方法描述:處理工具列按鈕點擊,執行影像順時針旋轉 90 度。載入影像後執行旋轉並儲存
            ,針對非黑白影像會設定 JPG 壓縮品質。最後重新繪製相關顯示元件並觸發點
            擊事件。
reassemble/view/treeView.pas
@@ -1,6 +1,6 @@
{ ==============================================================================
  方法名稱:DrawDocItem2
  引用相依:FileExists, LoadFromFile, DirectoryExists
  引用相依:DirectoryExists, FileExists, LoadFromFile
  方法描述:在樹狀結構中繪製指定案件的文件與表單節點。讀取 CaseDocNo.dat 取得文件
            清單,逐一建立文件節點並根據引用狀態設定圖示。接著在文件節點下建立表單
            節點,區分制式表單與自定義表單。包含入庫過濾與附件處理,是樹狀 UI 呈現
@@ -506,8 +506,8 @@
{ ==============================================================================
  方法名稱:TreeView1DragDrop
  引用相依:RenameFile, CopyFile, DeleteImageFile, LoadFromFile, SaveToFile, _De
            lTree, ReSortFileName
  引用相依:CopyFile, DeleteImageFile, LoadFromFile, ReSortFileName, RenameFile,
             SaveToFile, _DelTree
  方法描述:處理樹狀結構的拖放操作,實現影像的快速歸類或跨案件移動。當使用者將影像
            縮圖拖至樹狀節點時,程序會判斷目標層級,執行實際的檔案搬移(Copy/Delete
            )與更名,同步更新 Context.dat 索引檔案,並重新整理樹狀顯示與筆數統計。
scripts/dist/CB_IMGPSScanImp.pas.bk.image.csv
@@ -5,10 +5,12 @@
[V],"Button6Click","CB_IMGPSScanImp.pas.bk","598","Rotate","","BarCodeRotate : Integer; //條碼要轉的角度"
[V],"Button6Click","CB_IMGPSScanImp.pas.bk","615","FJpgCompression","","FJpgCompression:integer;// 20171211 jpg to tif çš„壓縮率"
[V],"GetSiteOMR","CB_IMGPSScanImp.pas.bk","674","GetSiteOMR","","function GetSiteOMR(FileName, Site: String;bt: Integer): Integer;"
[V],"ImageReSize_FormID","CB_IMGPSScanImp.pas.bk","742","ImageResize","","Procedure ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放"
[V],"ImageReSize_FormID","CB_IMGPSScanImp.pas.bk","742","ImageReSize_FormID","","Procedure ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放"
[V],"ImageReSize_tmp","CB_IMGPSScanImp.pas.bk","743","ImageResize","","Procedure ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)"
[V],"ImageReSize_tmp","CB_IMGPSScanImp.pas.bk","743","ImageReSize_tmp","","Procedure ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)"
[V],"CheckNeedCrop","CB_IMGPSScanImp.pas.bk","804","TDibGraphic","","Function CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像"
[V],"CheckNeedCrop","CB_IMGPSScanImp.pas.bk","804","CheckNeedCrop","","Function CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像"
[V],"CheckNeedCrop","CB_IMGPSScanImp.pas.bk","804","TDibGraphic","","Function CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像"
[V],"Initialize","CB_IMGPSScanImp.pas.bk","1044","TDibGraphic","","EnPrint,  { for TEnvisionPrintMode, TDibGraphicPrinter }"
[V],"TCB_IMGPSScanX.ISB1ImageMouseUp","CB_IMGPSScanImp.pas.bk","1505","TJpegGraphic","","TJpegGraphic(TImageScrollBox(Sender).Graphic).SaveQuality := 30;"
[V],"TCB_IMGPSScanX.ISB1ImageMouseUp","CB_IMGPSScanImp.pas.bk","1505","SaveQuality","","TJpegGraphic(TImageScrollBox(Sender).Graphic).SaveQuality := 30;"
@@ -27,8 +29,8 @@
[V],"TCB_IMGPSScanX.PM104Click","CB_IMGPSScanImp.pas.bk","3005","ifColor25","","else if iGraphic.ImageFormat= ifColor256 then"
[V],"TCB_IMGPSScanX.PM104Click","CB_IMGPSScanImp.pas.bk","3008","ConvertToGray","","ConverttoGray(iGraphic);"
[V],"TCB_IMGPSScanX.PM104Click","CB_IMGPSScanImp.pas.bk","3009","tcJpeg","","iGraphic.Compression:=tcJPEG;"
[V],"TCB_IMGPSScanX.PM104Click","CB_IMGPSScanImp.pas.bk","3012","ifTrueColor","","else if (iGraphic.ImageFormat = ifTrueColor) or (iGraphic.ImageFormat = ifGray256) then"
[V],"TCB_IMGPSScanX.PM104Click","CB_IMGPSScanImp.pas.bk","3012","ifGray256","","else if (iGraphic.ImageFormat = ifTrueColor) or (iGraphic.ImageFormat = ifGray256) then"
[V],"TCB_IMGPSScanX.PM104Click","CB_IMGPSScanImp.pas.bk","3012","ifTrueColor","","else if (iGraphic.ImageFormat = ifTrueColor) or (iGraphic.ImageFormat = ifGray256) then"
[V],"TCB_IMGPSScanX.PM104Click","CB_IMGPSScanImp.pas.bk","3050","SaveQuality","","JpgGr.SaveQuality := 30;"
[V],"TCB_IMGPSScanX.PM301Click","CB_IMGPSScanImp.pas.bk","3475","ifBlackWhite","","ScanColor := ifBlackWhite;"
[V],"TCB_IMGPSScanX.PM302Click","CB_IMGPSScanImp.pas.bk","3483","ifGray256","","ScanColor := ifGray256;"
@@ -62,14 +64,18 @@
[V],"TCB_IMGPSScanX.GetSiteOMR","CB_IMGPSScanImp.pas.bk","5222","Get_OMR","","result := Get_OMR(ISB_BW.Graphic,OMRRect);"
[V],"TCB_IMGPSScanX.GetDefScanIni","CB_IMGPSScanImp.pas.bk","5366","Rotate","","Else if GetSQLData(WORK_INF_List,'PARA_NO',i) = 'SCAN_ROTATE_MODE' Then //掃瞄時旋轉角度"
[V],"TCB_IMGPSScanX.GetDefScanIni","CB_IMGPSScanImp.pas.bk","5462","FJpgCompression","","FJpgCompression := StrToInt(PARA_CONTENT);"
[V],"TCB_IMGPSScanX.CaseReSize","CB_IMGPSScanImp.pas.bk","6336","ImageResize","","ImageReSize_FormID(CaseID,FileName);  //依十字定位點做縮放"
[V],"TCB_IMGPSScanX.CaseReSize","CB_IMGPSScanImp.pas.bk","6336","ImageReSize_FormID","","ImageReSize_FormID(CaseID,FileName);  //依十字定位點做縮放"
[V],"TCB_IMGPSScanX.ImageReSize_FormID","CB_IMGPSScanImp.pas.bk","6343","ImageResize","","Procedure TCB_IMGPSScanX.ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放"
[V],"TCB_IMGPSScanX.ImageReSize_FormID","CB_IMGPSScanImp.pas.bk","6343","ImageReSize_FormID","","Procedure TCB_IMGPSScanX.ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放"
[V],"TCB_IMGPSScanX.ImageReSize_FormID","CB_IMGPSScanImp.pas.bk","6376","FindPoint","","//FindPoint(ImageScrollBox1.Graphic,UpLPoint,UpRPoint,DownLPoint,NowW,NowH);"
[V],"TCB_IMGPSScanX.ImageReSize_FormID","CB_IMGPSScanImp.pas.bk","6379","CheckSize","","SizeStr := CheckSize(ISB_BW,UpLPoint,UpRPoint,DownLPoint,DW,DH);"
[V],"TCB_IMGPSScanX.ImageReSize_tmp","CB_IMGPSScanImp.pas.bk","6421","ImageResize","","Procedure TCB_IMGPSScanX.ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)"
[V],"TCB_IMGPSScanX.ImageReSize_tmp","CB_IMGPSScanImp.pas.bk","6421","ImageReSize_tmp","","Procedure TCB_IMGPSScanX.ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)"
[V],"TCB_IMGPSScanX.ImageReSize_tmp","CB_IMGPSScanImp.pas.bk","6437","CheckSize","","SizeStr := CheckSize(ImageScrollBox1,UpLPoint,UpRPoint,DownLPoint,DW,DH);"
[V],"TCB_IMGPSScanX.ImageScrollBox1NewGraphic","CB_IMGPSScanImp.pas.bk","6447","TDibGraphic","","procedure TCB_IMGPSScanX.ImageScrollBox1NewGraphic(const Graphic: TDibGraphic);"
[V],"TCB_IMGPSScanX.ImageScrollBox1NewGraphic","CB_IMGPSScanImp.pas.bk","6453","ConvertToBW","","ConvertToBW(ISB_BW.Graphic);"
[V],"TCB_IMGPSScanX.WriteResize","CB_IMGPSScanImp.pas.bk","8026","GetTag","","TagTxt := GetTag(ImgName);"
[V],"TCB_IMGPSScanX.ScanGrayCBClick","CB_IMGPSScanImp.pas.bk","8573","ifGray256","","ScanColor:=ifGray256;"
[V],"TCB_IMGPSScanX.ScanGrayCBClick","CB_IMGPSScanImp.pas.bk","8579","ifBlackWhite","","ScanColor := ifBlackWhite;"
[V],"TCB_IMGPSScanX.ScanGrayCBClick","CB_IMGPSScanImp.pas.bk","8591","ifTrueColor","","ScanColor := ifTrueColor ;"
@@ -78,11 +84,12 @@
[V],"TCB_IMGPSScanX.initParameter","CB_IMGPSScanImp.pas.bk","9301","ifTrueColor","","ScanColor := ifTrueColor ;"
[V],"TCB_IMGPSScanX.SmoothCBClick","CB_IMGPSScanImp.pas.bk","9927","Image_Smooth","","Image_Smooth(ISB1.Graphic);"
[V],"TCB_IMGPSScanX.Case2Mask","CB_IMGPSScanImp.pas.bk","9962","FindPoint","","//ParserPoint(CropMpsV.FindPoint(Anchor));"
[V],"TCB_IMGPSScanX.CheckNeedCrop","CB_IMGPSScanImp.pas.bk","9996","TDibGraphic","","Function TCB_IMGPSScanX.CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像"
[V],"TCB_IMGPSScanX.CheckNeedCrop","CB_IMGPSScanImp.pas.bk","9996","CheckNeedCrop","","Function TCB_IMGPSScanX.CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像"
[V],"TCB_IMGPSScanX.CheckNeedCrop","CB_IMGPSScanImp.pas.bk","9996","TDibGraphic","","Function TCB_IMGPSScanX.CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像"
[V],"TCB_IMGPSScanX.PrintImg","CB_IMGPSScanImp.pas.bk","10223","TDibGraphic","","GraphicPrinter : TDibGraphicPrinter;"
[V],"PrintWithManualPrintJob","CB_IMGPSScanImp.pas.bk","10241","TDibGraphic","","property of the TDibGraphicPrinter object is used to specify the"
[V],"PrintWithAutoPrintJob","CB_IMGPSScanImp.pas.bk","10274","TDibGraphic","","GraphicPrinter := TDibGraphicPrinter.Create;"
[V],"TCB_IMGPSScanX.OMRCheckCase","CB_IMGPSScanImp.pas.bk","12510","ImageResize","","ImageReSize_FormID(CaseID,OMRFile);"
[V],"TCB_IMGPSScanX.OMRCheckCase","CB_IMGPSScanImp.pas.bk","12510","ImageReSize_FormID","","ImageReSize_FormID(CaseID,OMRFile);"
[V],"TCB_IMGPSScanX.OMRCheckCase","CB_IMGPSScanImp.pas.bk","12545","GetSiteOMR","","//Showmessage(ColCName +','+ inttostr(OMRMpsV1.GetSiteOMR(ImageSavePath+CaseID+'\upload\'+OMRFile,Site))+','+inttostr(Pixel + SafePixel));"
[V],"TCB_IMGPSScanX.OMRCheckCase","CB_IMGPSScanImp.pas.bk","12548","FindPoint","","//FindPoint(ISB_BW.Graphic,UpLPoint,UpRPoint,DownLPoint,ANCHOR);"
scripts/dist/ErrList.pas.image.csv
@@ -1,5 +1,8 @@
[ ],方法名,所在檔名,行號,引用了什麼相依,方法描述(請手動填寫),原始程式碼片段
[V],"ImageScrollBox1NewGraphic","ErrList.pas","109","TDibGraphic","","procedure ImageScrollBox1NewGraphic(const Graphic: TDibGraphic);"
[V],"TErrlistForm.GetOMRErrini","ErrList.pas","487","FindPoint","","//FindPoint(ImageScrollBox1.Graphic,UpLPoint,UpRPoint,DownLPoint,'NONE');"
[V],"TErrlistForm.GetOMRErrini","ErrList.pas","493","ImageResize","","ImageResize(ImageScrollBox1.Graphic,ISB_BW.Graphic.Width,ISB_BW.Graphic.Height);"
[V],"TErrlistForm.ShowOMRErr","ErrList.pas","549","ShowKeyinRect","","//ShowKeyinRect(ImageScrollBox1,iRect);"
[V],"TErrlistForm.ImageScrollBox1NewGraphic","ErrList.pas","553","TDibGraphic","","procedure TErrlistForm.ImageScrollBox1NewGraphic(const Graphic: TDibGraphic);"
[V],"TErrlistForm.ImageScrollBox1NewGraphic","ErrList.pas","559","ConvertToBW","","ConvertToBW(ISB_BW.Graphic);"
[V],"TErrlistForm.ImageScrollBox1NewGraphic","ErrList.pas","559","ConvertToBW","","ConvertToBW(ISB_BW.Graphic);"
[V],"TErrlistForm.ShowRelaOMRErr","ErrList.pas","586","ShowKeyinRect","","//ShowKeyinRect(ImageScrollBox2,iRect);"
scripts/dist/deps.all.csv
@@ -5,10 +5,12 @@
[V],Button6Click,CB_IMGPSScanImp.pas.bk,598,Rotate,,BarCodeRotate : Integer; //條碼要轉的角度
[V],Button6Click,CB_IMGPSScanImp.pas.bk,615,FJpgCompression,,FJpgCompression:integer;// 20171211 jpg to tif çš„壓縮率
[V],GetSiteOMR,CB_IMGPSScanImp.pas.bk,674,GetSiteOMR,,function GetSiteOMR(FileName, Site: String;bt: Integer): Integer;
[V],ImageReSize_FormID,CB_IMGPSScanImp.pas.bk,742,ImageResize,,Procedure ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放
[V],ImageReSize_FormID,CB_IMGPSScanImp.pas.bk,742,ImageReSize_FormID,,Procedure ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放
[V],ImageReSize_tmp,CB_IMGPSScanImp.pas.bk,743,ImageResize,,Procedure ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)
[V],ImageReSize_tmp,CB_IMGPSScanImp.pas.bk,743,ImageReSize_tmp,,Procedure ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)
[V],CheckNeedCrop,CB_IMGPSScanImp.pas.bk,804,TDibGraphic,,Function CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像
[V],CheckNeedCrop,CB_IMGPSScanImp.pas.bk,804,CheckNeedCrop,,Function CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像
[V],CheckNeedCrop,CB_IMGPSScanImp.pas.bk,804,TDibGraphic,,Function CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像
[V],Initialize,CB_IMGPSScanImp.pas.bk,1044,TDibGraphic,,EnPrint,  { for TEnvisionPrintMode, TDibGraphicPrinter }
[V],TCB_IMGPSScanX.ISB1ImageMouseUp,CB_IMGPSScanImp.pas.bk,1505,TJpegGraphic,,TJpegGraphic(TImageScrollBox(Sender).Graphic).SaveQuality := 30;
[V],TCB_IMGPSScanX.ISB1ImageMouseUp,CB_IMGPSScanImp.pas.bk,1505,SaveQuality,,TJpegGraphic(TImageScrollBox(Sender).Graphic).SaveQuality := 30;
@@ -27,8 +29,8 @@
[V],TCB_IMGPSScanX.PM104Click,CB_IMGPSScanImp.pas.bk,3005,ifColor25,,else if iGraphic.ImageFormat= ifColor256 then
[V],TCB_IMGPSScanX.PM104Click,CB_IMGPSScanImp.pas.bk,3008,ConvertToGray,,ConverttoGray(iGraphic);
[V],TCB_IMGPSScanX.PM104Click,CB_IMGPSScanImp.pas.bk,3009,tcJpeg,,iGraphic.Compression:=tcJPEG;
[V],TCB_IMGPSScanX.PM104Click,CB_IMGPSScanImp.pas.bk,3012,ifTrueColor,,else if (iGraphic.ImageFormat = ifTrueColor) or (iGraphic.ImageFormat = ifGray256) then
[V],TCB_IMGPSScanX.PM104Click,CB_IMGPSScanImp.pas.bk,3012,ifGray256,,else if (iGraphic.ImageFormat = ifTrueColor) or (iGraphic.ImageFormat = ifGray256) then
[V],TCB_IMGPSScanX.PM104Click,CB_IMGPSScanImp.pas.bk,3012,ifTrueColor,,else if (iGraphic.ImageFormat = ifTrueColor) or (iGraphic.ImageFormat = ifGray256) then
[V],TCB_IMGPSScanX.PM104Click,CB_IMGPSScanImp.pas.bk,3050,SaveQuality,,JpgGr.SaveQuality := 30;
[V],TCB_IMGPSScanX.PM301Click,CB_IMGPSScanImp.pas.bk,3475,ifBlackWhite,,ScanColor := ifBlackWhite;
[V],TCB_IMGPSScanX.PM302Click,CB_IMGPSScanImp.pas.bk,3483,ifGray256,,ScanColor := ifGray256;
@@ -62,14 +64,18 @@
[V],TCB_IMGPSScanX.GetSiteOMR,CB_IMGPSScanImp.pas.bk,5222,Get_OMR,,result := Get_OMR(ISB_BW.Graphic,OMRRect);
[V],TCB_IMGPSScanX.GetDefScanIni,CB_IMGPSScanImp.pas.bk,5366,Rotate,,Else if GetSQLData(WORK_INF_List,'PARA_NO',i) = 'SCAN_ROTATE_MODE' Then //掃瞄時旋轉角度
[V],TCB_IMGPSScanX.GetDefScanIni,CB_IMGPSScanImp.pas.bk,5462,FJpgCompression,,FJpgCompression := StrToInt(PARA_CONTENT);
[V],TCB_IMGPSScanX.CaseReSize,CB_IMGPSScanImp.pas.bk,6336,ImageResize,,ImageReSize_FormID(CaseID,FileName);  //依十字定位點做縮放
[V],TCB_IMGPSScanX.CaseReSize,CB_IMGPSScanImp.pas.bk,6336,ImageReSize_FormID,,ImageReSize_FormID(CaseID,FileName);  //依十字定位點做縮放
[V],TCB_IMGPSScanX.ImageReSize_FormID,CB_IMGPSScanImp.pas.bk,6343,ImageResize,,Procedure TCB_IMGPSScanX.ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放
[V],TCB_IMGPSScanX.ImageReSize_FormID,CB_IMGPSScanImp.pas.bk,6343,ImageReSize_FormID,,Procedure TCB_IMGPSScanX.ImageReSize_FormID(CaseID,FileName:String);  //依十字定位點做縮放
[V],TCB_IMGPSScanX.ImageReSize_FormID,CB_IMGPSScanImp.pas.bk,6376,FindPoint,,//FindPoint(ImageScrollBox1.Graphic,UpLPoint,UpRPoint,DownLPoint,NowW,NowH);
[V],TCB_IMGPSScanX.ImageReSize_FormID,CB_IMGPSScanImp.pas.bk,6379,CheckSize,,SizeStr := CheckSize(ISB_BW,UpLPoint,UpRPoint,DownLPoint,DW,DH);
[V],TCB_IMGPSScanX.ImageReSize_tmp,CB_IMGPSScanImp.pas.bk,6421,ImageResize,,Procedure TCB_IMGPSScanX.ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)
[V],TCB_IMGPSScanX.ImageReSize_tmp,CB_IMGPSScanImp.pas.bk,6421,ImageReSize_tmp,,Procedure TCB_IMGPSScanX.ImageReSize_tmp(FormID,FileName:String);  //依十字定位點做縮放(暫存檔)
[V],TCB_IMGPSScanX.ImageReSize_tmp,CB_IMGPSScanImp.pas.bk,6437,CheckSize,,SizeStr := CheckSize(ImageScrollBox1,UpLPoint,UpRPoint,DownLPoint,DW,DH);
[V],TCB_IMGPSScanX.ImageScrollBox1NewGraphic,CB_IMGPSScanImp.pas.bk,6447,TDibGraphic,,procedure TCB_IMGPSScanX.ImageScrollBox1NewGraphic(const Graphic: TDibGraphic);
[V],TCB_IMGPSScanX.ImageScrollBox1NewGraphic,CB_IMGPSScanImp.pas.bk,6453,ConvertToBW,,ConvertToBW(ISB_BW.Graphic);
[V],TCB_IMGPSScanX.WriteResize,CB_IMGPSScanImp.pas.bk,8026,GetTag,,TagTxt := GetTag(ImgName);
[V],TCB_IMGPSScanX.ScanGrayCBClick,CB_IMGPSScanImp.pas.bk,8573,ifGray256,,ScanColor:=ifGray256;
[V],TCB_IMGPSScanX.ScanGrayCBClick,CB_IMGPSScanImp.pas.bk,8579,ifBlackWhite,,ScanColor := ifBlackWhite;
[V],TCB_IMGPSScanX.ScanGrayCBClick,CB_IMGPSScanImp.pas.bk,8591,ifTrueColor,,ScanColor := ifTrueColor ;
@@ -78,11 +84,12 @@
[V],TCB_IMGPSScanX.initParameter,CB_IMGPSScanImp.pas.bk,9301,ifTrueColor,,ScanColor := ifTrueColor ;
[V],TCB_IMGPSScanX.SmoothCBClick,CB_IMGPSScanImp.pas.bk,9927,Image_Smooth,,Image_Smooth(ISB1.Graphic);
[V],TCB_IMGPSScanX.Case2Mask,CB_IMGPSScanImp.pas.bk,9962,FindPoint,,//ParserPoint(CropMpsV.FindPoint(Anchor));
[V],TCB_IMGPSScanX.CheckNeedCrop,CB_IMGPSScanImp.pas.bk,9996,TDibGraphic,,Function TCB_IMGPSScanX.CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像
[V],TCB_IMGPSScanX.CheckNeedCrop,CB_IMGPSScanImp.pas.bk,9996,CheckNeedCrop,,Function TCB_IMGPSScanX.CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像
[V],TCB_IMGPSScanX.CheckNeedCrop,CB_IMGPSScanImp.pas.bk,9996,TDibGraphic,,Function TCB_IMGPSScanX.CheckNeedCrop(Graphic:TDibGraphic):Boolean; //是否是A3要切影像
[V],TCB_IMGPSScanX.PrintImg,CB_IMGPSScanImp.pas.bk,10223,TDibGraphic,,GraphicPrinter : TDibGraphicPrinter;
[V],PrintWithManualPrintJob,CB_IMGPSScanImp.pas.bk,10241,TDibGraphic,,property of the TDibGraphicPrinter object is used to specify the
[V],PrintWithAutoPrintJob,CB_IMGPSScanImp.pas.bk,10274,TDibGraphic,,GraphicPrinter := TDibGraphicPrinter.Create;
[V],TCB_IMGPSScanX.OMRCheckCase,CB_IMGPSScanImp.pas.bk,12510,ImageResize,,ImageReSize_FormID(CaseID,OMRFile);
[V],TCB_IMGPSScanX.OMRCheckCase,CB_IMGPSScanImp.pas.bk,12510,ImageReSize_FormID,,ImageReSize_FormID(CaseID,OMRFile);
[V],TCB_IMGPSScanX.OMRCheckCase,CB_IMGPSScanImp.pas.bk,12545,GetSiteOMR,,//Showmessage(ColCName +','+ inttostr(OMRMpsV1.GetSiteOMR(ImageSavePath+CaseID+'\upload\'+OMRFile,Site))+','+inttostr(Pixel + SafePixel));
[V],TCB_IMGPSScanX.OMRCheckCase,CB_IMGPSScanImp.pas.bk,12548,FindPoint,,//FindPoint(ISB_BW.Graphic,UpLPoint,UpRPoint,DownLPoint,ANCHOR);
@@ -103,8 +110,11 @@
[V],TCB_IMGPSScanX.Set_scancolor,CB_IMGPSScanImp.pas.bk,16372,ifTrueColor,,ScanColor := ifTrueColor ;
[V],ImageScrollBox1NewGraphic,ErrList.pas,109,TDibGraphic,,procedure ImageScrollBox1NewGraphic(const Graphic: TDibGraphic);
[V],TErrlistForm.GetOMRErrini,ErrList.pas,487,FindPoint,,//FindPoint(ImageScrollBox1.Graphic,UpLPoint,UpRPoint,DownLPoint,'NONE');
[V],TErrlistForm.GetOMRErrini,ErrList.pas,493,ImageResize,,ImageResize(ImageScrollBox1.Graphic,ISB_BW.Graphic.Width,ISB_BW.Graphic.Height);
[V],TErrlistForm.ShowOMRErr,ErrList.pas,549,ShowKeyinRect,,//ShowKeyinRect(ImageScrollBox1,iRect);
[V],TErrlistForm.ImageScrollBox1NewGraphic,ErrList.pas,553,TDibGraphic,,procedure TErrlistForm.ImageScrollBox1NewGraphic(const Graphic: TDibGraphic);
[V],TErrlistForm.ImageScrollBox1NewGraphic,ErrList.pas,559,ConvertToBW,,ConvertToBW(ISB_BW.Graphic);
[V],TErrlistForm.ShowRelaOMRErr,ErrList.pas,586,ShowKeyinRect,,//ShowKeyinRect(ImageScrollBox2,iRect);
[V],TOldCaseInfoForm.ShowImage,OldCaseInfo.pas,333,DpiResize,,DpiResize(ISB.Graphic,36,False);
[V],ReSortFileName_New,CB_IMGPSScanImp.pas.bk,726,ReSortFileName,,Procedure ReSortFileName_New(Path:String); //檔名重新排序
[V],ReSortFileName,CB_IMGPSScanImp.pas.bk,727,ReSortFileName,,Procedure ReSortFileName(Path:String); //檔名重新排序
scripts/list_image_dependencies.js
@@ -12,49 +12,67 @@
const exclusions = Array.from(new Set([
  'ScanRotate',
  'ImageFormat <> ifBlackWhite',
])).join('|')
// 您提供的關鍵字 (以 | 分隔)
const keywordsStr = Array.from(new Set([
  'TTiffGraphic',
  'TDibGraphic',
  'DeskewImg',
  'Rotate',
  'BrightnessImg', // iis_image_process
  'CheckNeedCrop',
  'CheckSize',
  'CleanupBorder',
  'Color2BW_RTS', //  iis_image_process
  'ConvertToBW',
  'ConvertToGray',
  'CreateReportImg_JSON', // iis_image_process
  'CreateStamp', // iis_image_process
  'CropImg',
  'DeskewImg',
  'DpiResize',
  'FilterColor', // iis_image_process
  'FindBlackPoint', // iis_image_process
  'FindPoint',
  'GetSiteOMR',
  'GetBlackSpots', // iis_image_process
  'GetSelectRect2String', // iis_image_process
  'GetTag', //
  'Get_OMR',
  'Gray2BW_RTS', // iis_image_process
  'ImageResize', // iis_image_process
  'ImageProcessor.anchorAnalyzer',
  'ImageProcessor.barcodeRecognizer',
  'ImageProcessor.converter',
  'ImageProcessor.transformer',
  'ImageReSize_FormID',
  'ImageReSize_tmp',
  'CheckNeedCrop',
  'ImageProcessor.transformer',
  'ConvertToBW',
  'ConvertToGray',
  'Image_Smooth',
  'NegativeImg',
  'CleanupBorder',
  'ImageProcessor.converter',
  'JpgReSize_Exif', // iis
  'MpsGetBarcode',
  'Get_OMR',
  'ImageProcessor.barcodeRecognizer',
  'FindPoint',
  'CheckSize',
  'GetSiteOMR',
  'ImageProcessor.anchorAnalyzer',
  'NegativeImg',
  'PrintBarcode', // iis
  'Rotate',
  'SetTag', // iis
  'ShowKeyinRect', // iis
  'TDibGraphic',
  'TJpegGraphic',
  'DpiResize',
  'TNBCleanupBorderTransform', // iis
  'TTiffGraphic',
  'TWatermarkTransform', // iis
  'Watermark1_Hong', // iis
  // 以下可省
  'SaveQuality',
  'FJpgCompression',
  'ifTrueColor',
  'ifGray256',
  'CleanupBorder',
  'ConvertToBW',
  'ConvertToGray',
  'FJpgCompression',
  'Image_Smooth',
  'NegativeImg',
  'CleanupBorder',
  'SaveQuality',
  'ifBlackWhite',
  'ifColor25',
  'ifGray256',
  'ifTrueColor',
  'tcGroup4',
  'tcPackBits',
  'tcJpeg',
  'ifColor25'
  'tcPackBits',
])).join('|')
// 執行腳本 (請確認您的目標檔案名稱,預設為 CB_IMGPSScanImp.pas)
const targetFiles = [
scripts/scanimpl_annalysis_json_deps.js
比對新檔案
@@ -0,0 +1,90 @@
const fs = require('fs');
const path = require('path');
const csvPath = 'scripts/dist/deps.all.csv';
const jsonDir = 'doc/curtis/prompt/scanimpl_analysis';
const modules = [
  'ScannerController',
  'BusinessLogic',
  'ImageProcessor',
  'TransportManager',
  'UIView'
];
function parseCSV(csv) {
  const lines = csv.split('\n');
  const depsMap = {};
  // Skip header
  for (let i = 1; i < lines.length; i++) {
    const line = lines[i].trim();
    if (!line) continue;
    // Simple CSV parser (not handling quoted commas as there shouldn't be any in this specific CSV structure)
    const parts = line.split(',');
    if (parts.length < 5) continue;
    const methodName = parts[1].trim();
    const dep = parts[4].trim();
    if (methodName && dep) {
      if (!depsMap[methodName]) {
        depsMap[methodName] = new Set();
      }
      depsMap[methodName].add(dep);
    }
  }
  // Convert sets to sorted arrays
  const result = {};
  for (const name in depsMap) {
    result[name] = Array.from(depsMap[name]).sort();
  }
  return result;
}
function updateJSONs() {
  const csvContent = fs.readFileSync(csvPath, 'utf8');
  const depsMap = parseCSV(csvContent);
  modules.forEach(mod => {
    const filePath = path.join(jsonDir, `scanimpl_annalysis.${mod}.json`);
    if (!fs.existsSync(filePath)) {
      console.log(`File not found: ${filePath}`);
      return;
    }
    let methods = JSON.parse(fs.readFileSync(filePath, 'utf8'));
    let updatedCount = 0;
    methods = methods.map(method => {
      // Find matching methodName in depsMap
      // matcher example: "function TCB_IMGPSScanX.GetSiteOMR("
      // methodName example: "GetSiteOMR"
      const foundDeps = new Set();
      for (const methodName in depsMap) {
        // Use regex to match exact method name within the matcher string
        const regex = new RegExp(`\\b${methodName}\\b`);
        if (regex.test(method.matcher)) {
          depsMap[methodName].forEach(d => foundDeps.add(d));
        }
      }
      if (foundDeps.size > 0) {
        method.deps = Array.from(foundDeps).sort();
        updatedCount++;
      } else {
        // Keep existing deps if any, or initialize to empty array if required
        if (!method.deps) method.deps = [];
      }
      return method;
    });
    fs.writeFileSync(filePath, JSON.stringify(methods, null, 2), 'utf8');
    console.log(`Updated ${updatedCount} methods in ${filePath}`);
  });
}
updateJSONs();