2017年4月3日 星期一

MacBook Pro掛了

可能發生原因

  • MacBook 散熱孔在螢幕/基座交接處,而且有部分散熱是靠鍵盤
  • 因為買了外接螢幕,在家裡比較常使用 ClamShell 模式,鍵盤剛好放在 MacBook 上
  • 如果玩遊戲或跑程式,MacBook 的溫度會顯著上升(但其實沒有到「燙」),可能造成以下故障(應該是螢幕掛了)

症狀

  • 通常平常是關蓋睡眠,所以首先發現開蓋後螢幕全黑
  • 直接按電源鍵重開機,有開機聲、鍵盤背光會亮,但螢幕一直是黑的
  • 接外接螢幕重開機,有開機聲,但讀取畫面跑一半突然螢幕變黑
  • 黑色畫面中直接輸入登入密碼再 Enter,無效
  • Rest PRAM/NVRAM
    • 開機時按 Win+Opt+P+R 直到第二次開機聲 → 無效
  • Reset SMC
    • 拔掉外接電源
    • 按住 Shift+Control+Option 及電源鍵10秒鐘後放開
    • 接外接電源
    • 嘗試開機 → 無效
  • 接外接螢幕(以下步驟都有接),開機時按 Shift 可進安全模式,當然有些功能會受限,但至少能上網(WiFi)
  • 接 macOS Sierra 的開機 USB,開機時按 Option 選擇 USB,用磁碟工具程式修復,顯示無錯誤
  • 開機時按 Command + C 可進 single-user mode,用 fsck -fy 指令修復,顯示無錯誤
  • 開機時按 D,檢測硬體說「顯示器可能發生問題」,參考代碼 VFD002
  • 有錯誤碼比較容易 google 到精確的資訊,發現很多人有一樣的問題,這台是 MacBook Pro 13" (Late 2013) 不在召回之列而且也過保固了,換螢幕要10000多整個不划算,到 iFixit 看零件也差不多價錢

解決方法

  • 接外接螢幕、外接電源,開機時看到讀取畫面時馬上關蓋變成 ClamShell 模式,可以順利到登入畫面、進入系統,但開蓋後螢幕又會黑掉,再關蓋也是一樣
  • 目前就關蓋當超薄主機的功能,可惜了 TrackPad 不能用(除非把螢幕鋸掉一半?)

2017年3月22日 星期三

Deskew - 掃描文件轉正

去接老婆時常常要多等個 10-20 分鐘,一問之下原來是掃描文件後,如果看到 pdf 檔裡文件太歪,就要重新掃描。一聽到這問題,直覺上就認為是簡單的 post processing 就能解決問題啊!目前用的方案為:

  • 掃描後的 pdf 檔
  • Ghostscript 將 pdf 轉成多張 tif
  • Deskew 將每個 tif 轉正
  • ImageMagick 將 tif 轉成 jpg,調整壓縮比及resolution,再以 ImageMagick 將 jpg 包成 pdf

曾經嘗試但未採用的方案:
  • Adobe Acrobat Pro
    • 付費軟體,得來不易
    • 沒有開 OCR 的話效果很差,但預設的 OCR 只有英文的
    • 手動旋轉不易調整
  • PDFill
    • 可以用水平線轉正
    • 乍看功能很多,但看首頁就知道,這個軟體的 UI 設計爛到不行,使用起來非常不直覺
  • Scan Tailor
    • 頗接近需求的軟體,可惜除了 deskew,還會把其他步驟綁在一起才能輸出(例如 Select Content),自動化抓好的話很順利,抓不好每張都要調整反正花更多時間
  • unpaper
    • 其實是第一個找到的,看起來功能很完整很強大,除了 deskew 外還有其他掃描文件常需要的校正功能,但因為是純命令列所以參數很多
    • 比較詭異的是 input / output 是少見的 pnm format (.pbm, .pgm, .ppm)
    • 嘗試了一下發現效果沒有想像中好,大概需要再調參數...
  • Photoshop
    • 修圖很好用,當然也能拉水平線轉正,但無法自動化用在 pdf 檔...
















2017年2月25日 星期六

Firefox WebExtension 入門 - 筆記 & 心得


緣起

  • Sleipnir 是日本人開發的瀏覽器,支援 IE 核心、免安裝、可設定很多熱鍵及滑鼠手勢,多年以來是用 2.99 版,因為第三版之後介面比較華麗(跑得比較慢)而且可以設定的項目反而變少。搭配的套件是 SeaHorse,也就是類似 Firefox 的 GreaseMonkey,用使用者自定的 JavaScript 去「後處理」網頁。一張爛圖可以用 Photoshop 修成好圖,爛網站也可以用 user script 修成好用的介面。
  • 因為用的是非常舊的版本,遇到較新版本的IE開始不是很相容,當然也有可能是院內網的問題,總之下定決心轉換跑道,希望用比較新的工具!
  • 剛開始嘗試寫成 Chrome 的 extension,但發現遇到的問題比 Firefox 多一些,於是又改寫成 Firefox 的 WebE xtension,還好後者本來就向前者致敬,不需要改很多東西。

背景知識

  • Firefox 以前的套件稱作 add-on,要查 add-on SDK;但每個頁面最上面都會提示你說這是舊的技術,建議改用 WebExtension
  • 最終的套件是用把所有檔案 zip 打包再改成 xpi 副檔名。在開發過程中,建議用Firefox Developer Edition,在 about:debug 頁面中可以直接 load 資料夾中的 manifest.json 作測試。
  • manifest.json 是整個套件最重要的檔案,包含這個套件的基本資訊、套件可以用到的權限、定義 content scripts 的 rules。很明顯的是一個 JSON 的結構。
  • content script 就是 user script,但rule是定義在 manifest.json 而不是在script 裡
    • matches:某個 script 要套用的網頁 URL 規則,要注意的是 URL 包含 scheme, host, path
    • run_at:通常等到網頁 loading 完才執行 script,但可以設定在 loading 完成前就執行
    • js:為了安全因素 content script 執行的 scope 是和網頁不同的,如果希望無痛轉移原本的 Sleipnir + SeaHorse,需要在 content script 中再把舊的 script 注入(inject)到網頁裡。
      • Chrome extension 的資料比 Firefox 多,詳見此篇必讀!
      • Content script 當作一種橋樑,一方面可以和套件的其他部分溝通,一方面可以和網頁溝通。
  • background script 是 Firefox 執行後就會在背景常駐的一個 script,作為一個中央指揮部,用來(暫時)儲存使用者的帳號/密碼、定時 refresh session id 以免被登出。這些功能本來是寫在背景執行的 AutoHotkey script,搭配 Sleipnir 開啟。
  • popup:容易誤解的名詞,是指(選擇性地)在右上角可以顯示一個圖示,按下圖示時會出現的框(事實上是一個網頁)
    • 要在 manifest.json 裡定義 browser_action
    • 網頁原始檔不能有任何 inline JavaScript,應該要用外部連結到另一個 js 檔(主要是 submit button 會用到)
  • popup、background script、content script 可以互相溝通,利用 browser.runtime.sendMessagebrowser.runtime.onMessage

結構

  • 啟動 firefox,預設首頁就是院內網登入頁;background script 直接開啟 popup,提示使用者輸入帳號/密碼
  • popup 用 http request 去檢查帳密能不能登入,如果登入成功,會將帳密傳給 background script 儲存,所以 firefox 還開著的時候都可以記憶帳密、快速登入
  • popup 也會 sendMessage 給開啟的登入頁的 content script,自動填入帳密;事實上,登入頁的 content script 會先向 background script 請求帳密,以達成自動登入
  • content script 可以指定數個 script 依序讀入,加上 content script 和 page script 是分開的,所以能 load 額外的 jQuery 或 jQuery UI,又不會和原本的網頁衝突
  • 針對 IE only 的網頁(例如需要用到 ActiveX 的頁面),裝了幾個類似 IE tab 的現成套件好像都沒有作用,目前先設計 content script 在 document_start 時就把網頁 title(會顯示在 firefox 的視窗 title)改成特定的文字(含 url),再由背景的 AutoHotkey script 去抓 title 中的 url,另外由 IE 開啟。當然,這招是很 low 的解法,還沒有考慮 cookie、session 等。

發佈/更新

  • manifest.json 裡的版本號更新,將所有檔案打包成 zip 檔
  • 套件中心登入,第一次上傳時,「如何散布此版本」選擇「您自行處理」,表示我要自己管理更新來源,而不是放在官方網站上,因為放在官網的套件需要人工審查,要排隊等.超.久!
  • 上傳後會自動審查,沒有重大錯誤就 OK 了,會提供簽章過的 xpi 檔,下載之(沒有簽章過的套件,是不能裝在一般 firefox)
  • 套件會抓 manifest.json 裡的 update_url 所連結的檔案(一定要 https 開頭的連結),格式也是 JSON,包含各版本的檔案連結;所以 updates.json 和 xpi 檔要放到一個網路空間(例如 dropbox),xpi 檔的連結寫進 updates.json,updates.json 的連結寫進 manifest.json
  • 整個發佈/更新流程可以用 web-ext 完成,但我還在研究中...


2016年11月24日 星期四

2016-11 Maldives


今年年初老婆就計劃要去Maldives,去Maldives當然是要接觸海洋,所以上半年開始就積極學習潛水。老婆及兩個同事,加上我湊數(我比較不擅長水中活動...)報名了潛水課程,除了很無聊但其實還是重要的投影片上課,先在南港運動中心的5米潛水池學習練習一陣子,然後到東北角(龍洞)海洋實習,再跟了綠島、小琉球潛水團。到出國前取得PADI的open water (OW)、advanced open water (AOW)、enriched air (高氧),總共27支氣瓶。

這次去的是Maldives的Club Med Kani島,潛水中心是Euro-Diver,由價目表可知:
  • 什麼都貴,租裝備好貴!只有蛙鞋不用錢
  • 全部都是船潛
  • 防寒衣只有短袖的 (shorty)
  • 高氧氣瓶不用加錢,超爽der~~
  • 6 days/13 dives 的方案比較划算
  • 每人都必備潛水電腦表、浮力袋

行前準備:

  • 因為我是大近視一定需要有度數的面鏡,就順便兩人都買了
  • 潛水電腦表有點貴,只買了一支
  • 向朋友借了一個浮力袋
  • 向教練借了:防寒衣 × 2、BCD × 2、調節器 × 2、潛水用水電筒 × 2、相機+潛水殼、潛水電腦表 × 1、潛水裝備箱
  • 潛水證照

實際行程及心得:

  • D1:搭長榮到新加坡,再搭新航到Maldives
    • 抵達時接近半夜了,從機場島坐船到Kani島約30分鐘
  • D2:
    • 下水前一定要有一次orientation dive確定潛水程度,然後orientation dive一定前一天要預約,所以這一天只能先玩浮潛,順便熟悉潛水相機
    • 浮潛、獨木舟、stand-up paddle、sailboat是免費的 (sailboat要先上一堂課才能自己出海),其他行程都要另外收費(而且都很貴)
    • 下午四點在潛水中心看證照、確認裝備(要求每個人都要浮力袋,所以租一個)、預約隔日潛水。潛水時間為上午8:30潛兩支,兩支中間會在船上休息、聊天,下午1:50潛一支,夜潛不一定有
  • D3:
    • 依規定早上orientation dive後,只能等下午正式潛水。剛好有大陸人上課,所以10點多開一趟!
    • 那邊都是套腳式蛙鞋,我的腳太嫩一天結束右腳第4指、腳跟直接磨破皮。接下來幾天靠著OK蹦+自己的普通棉襪+Acetaminophen撐過 Orz
  • D4 ~ D6
    • 如果四天都潛滿潛好,扣掉orientation dive後只有11支,還好很幸運地D5、D6都有夜潛湊到13支優惠價,其中D6的夜潛大概是老闆被我們的瘋狂潛水所感動,才三人+教練就成行了(通常至少要四人)
    • 教練包括老闆(法)、老闆娘(父母是越南人但她是土生土長法國人)、法籍男、日籍女、日籍男、埃及男...等
    • 早上船潛滿多人,下午較少人。法國人最多,俄國人最猛(完全不穿防寒衣)。
    • 貴族級服務:BCD/調節器/氣瓶在岸上組裝好、剩下的裝備放在個人的籃子裡,船員會幫忙搬到船上。潛點到了會搬氣瓶到椅子上幫忙穿BCD,潛完了上船也是幫忙卸裝備、換下一支氣瓶或拆BCD/調節器。回碼頭也是船員把裝備搬下船。
    • 通常到達潛點後,教練先自由潛水確定潛點,確定大家直接下潛就到位。通常下去後就是順著流飄沿路找生物,或是抓著海底看很久的manta rays。在水下教練是用不同的手勢表達發現的生物,通常是海龜、鯊魚、manta、龍蝦、壯觀的魚群。
    • 每次潛完會記錄最大深度、潛水時間、氣瓶殘壓,最後一天可以領一張潛水記錄,內容還包括每次的潛水地點、教練。
  • D7
    • 搭飛機前一天不能潛水,繼續進行其他水面活動~
  • D8 ~ D10:Singapore
    • 去了環球影城、夜間動物園、濱海灣花園、水族館、玩Luge
    • 第一天有時差,相當於只在飛機睡四小時,超累 Orz
    • 水族館非常棒,尤其最裡面的大池+餵食秀,那麼多(肥)魚的爭食畫面是平常潛水無法看到的
    • Luge在紐西蘭玩過了,好玩但就是貴啊...
    • 第一天堅持搭公共運輸走好多路讓腳的傷口有點惡化,後兩天都直接叫Uber!
結論
  • 免錢的最貴,下次還是要自己帶套鞋、蛙鞋 T_T




2016年10月11日 星期二

AutoHotkey: traditional & expression

在 AutoHotkey 的官方說明檔講到變數Variables時,就開始提到 traditional & expression。Traditional 指的是 AutoHotkey 的傳統,寫什麼就是什麼,剛開始會覺得方便,但偶爾就會遇到一些陷阱,在文末會提到。

字串

a = test1
b := "test2"

  • Traditional 就是寫什麼是什麼(但忽略開頭的空白)
  • Expression 的話就是要有一對雙引號 " 夾住字串,相當於要從雙引號中取出字串


a = add
b = test%a%
c := "test" . a

  • Traditional 字串串接變數要用兩個 % 把變數的值先取出
  • Expression 的話可以用一個點 . 或一個空白鍵相隔,就是字串串接

a = c
bc = test
d := b%a%
  • 前兩個 traditional 很直覺,但第三行是什麼? %a% 是對 a 取值,所以是 c 這個字,右邊就是 bc,因為是 expression 所以要對 bc 取值,所以 d 的值也是 test

    流程控制

    if a > 2
        c = 1
    else if a = b
        c = 2
    • Traditional 模式,運算元左邊視為變數,運算元右邊視為普通文字,所以第一行會取出 a 的值,如果比 2 大則為 true。第三行右邊的 b 就只是一個 b,a 的值如果就是 b 這個字的話會是 true

    if (a>b)
        c = 1
    else if StrLen(a)
        c = 2

    Command & Function

    MsgBox,,,test,
    MsgBox,,,% "test" var1 var2
    MsgBox,test
    MsgBox % var
    MsgBox % var%a%
    • 以 MsgBox 這個典型的 command 來說,最左邊是 command 的名稱,右邊以逗點分隔參數,參數區預設為 traditional 模式,所以可以空著不寫,AutoHotkey 會幫你填入最常用的預設值
    • 如果某一個參數之後就全部空著,那麼連逗號都不必寫,全部空掉也可以
    • 第二行的第三個參數先一個%再接一個空格(或tab),表示這個參數變成 expression 模式,可以自由串接變數
    • 第三、四、五行僅限於部分 command,只用第一個參數代表省略其他的參數,一樣也區分 traditional 或 expression 模式

    SubStr( var, 1, 2)
    SubStr( var%a%, 1, 2)
    SubStr( "test", 2)
    SubStr( "test", 2, )
    • Function 是以 function 名稱加上一對小括號,參數也是以逗號分隔但只限expression模式。
    • 空的參數應該要以 "" 表示,或者就不要有逗號,第三行是錯誤示範

      常見陷阱


      • 因為 AutoHotkey 不用宣告變數類別,所以數字類的值有可能是字串,也可能是數字(整數、小數等)
      • a 是 01 的字串
      • b 牽涉到數學運算,所以變成數字 1
      • c 牽涉到字串串接,所以又變為 01 字串
      • 在 AutoHotkey_L 版本可以用到object類的陣列,如果key是像數字的字串,會自動被轉成數字,所以 r[a] 事實上變成 r[1] 而不是 r["01"] 也不是 r["1"],除非指定 r["" a] 才是 r["01"]


        • if 之後如果有兩行以下則要用大括號 { } 包起來,但如果 if 是traditional模式,事實上第一行被視為 a 的值是不是 2 { 這個字串,也就是 { 被誤解了。所以原本的 } 就會被 AutoHotkey 解讀為多出來的,而這個 if 影響的只有 c=1 不包括 d=1 (因為根本沒有大括號包起來)
        • 第二個 if 是 expression 模式,而且很有效率順便給 a 變數一個值。可是 := 這個運算元的優先順序是很低的,a 的值會變成 (c+1) && (d>0) 整串的結果,而不是 c+1