顯示具有 outlook 標籤的文章。 顯示所有文章
顯示具有 outlook 標籤的文章。 顯示所有文章

2025年5月11日 星期日

Outlook Mail 的製作紀錄 - MSO conditional comments 及 VML (Vector Markup Language)

在製作 Outlook HTML Mail 時,PC 版 Outlook 有著許多的限制,
像是有限的 CSS 支援 (例如不支援 border-radius,要以 inline-css 的方式)、
最好的排版方式是使用 <table> 等,
所以在設計版面上面跟普通的 html page 有著蠻大的不同。

今天想要紀錄的是如何製作一般 Web Mail (如 Gmail、Web 版 Outlook 等) 及 PC 版 Outlook 都可正常顯示的 "在圓角長方形上面的一個有帶連結的圓角長方形按鈕"。

成果圖會像這樣 (還是有些許差異,但大體上設計沒有跑掉):

如果是普通的 Web Mail,HTML 的部份可以這樣設計:

<div style="border-radius: 10px; background-color: #f4f4f4; padding-top: 20px; padding-bottom: 20px; padding-right: 20px; padding-left: 20px;">
	<a href="https://xxx.xxx.xxx" style="background-color: #000000; color: #ffffff; border-radius: 10px; padding-top:10px; padding-bottom:10px; padding-right:20px; padding-left:20px; font-size: 16px; text-decoration: none;">
		Click This Button
	</a>
 </div>

在 Gmail 的樣子如下(Web Outlook 也差不多)

但是 PC Outlook 沒辦法正常處理 border-raidus, padding 等問題,會長得像這樣:

所以需要對 PC Outlook 做特別處理。

這裡我們使用了兩個知識點來處理,分別是用來分辨裝置是否為 PC Outlook 的 MSO conditional comments 和用來繪置圖形的 VML (Vector Markup Language) 。

MSO conditional comments 可以寫在 HTML 裡,可以用來判斷裝置是否為 PC Outlook,
寫法如下,對一般網頁瀏覽器來說,專鬥給 PC Outlook 看的部份因為被 <!-- 和 --> 包圍所以會被視為注解,而給一般網頁瀏覽器看的部份則不會在 PC Outlook 上顯示。
這樣我們就可以區分 PC Outlook 和一般網頁瀏覽器了。

<!--[if mso]>
給 PC Outlook 看的 Content,PC Outlook 會顯示,而一般網頁瀏覽器會視其為注解,因為被 <!-- 和 --> 包圍
<![endif]-->

<!--[if !mso]><!-- -->
給一般網頁瀏覽器看的 Content,PC Outlook 不會顯示
<!--<![endif]-->

再來是 VML (Vector Markup Language) ,雖然 PC Outlook 可以顯示 HTML,但它使用的其實是 Word 的 Render Engine,而其可以使用 VML (Vector Markup Language) 來繪製圖形。
所以我們這邊可以使用 VML 取代 HTML 來繪製我們要的圓角連結按鈕,更詳細的 VML 圖形繪制語法可以參考 使用預先定義的圖形 - Win32 apps | Microsoft Learn

在這裡我使用了 <v:roundrect> 來繪製圓角長方形。
第一個 <v:roundrect> 設定如下:

  1. 用 strokecolor 和 fillcolor 設定邊框及底色為灰色 (#f4f4f4) 。
  2. 用 arcsize 設定圓角半徑。
  3. 用 style 設定 width 和 height。
  4. 用 style 設定 position: absoulte 用來等下將下一個 <v:roundrect> 疊在上面顯示。
第二個 <v:roundrect> 設定如下:

    1. 用 strokecolor 和 fillcolor 設定邊框及底色為黑色 (#000000) 。
    2. 用 arcsize 設定圓角半徑。
    3. 用 style 設定 width 和 height。
    4. 用 style 設定 position: absoulte, 配合 top 和 left 設定擺放位置,這樣就可以將這個 <v:roundrect> 放置在第一個 <v:roundrect> 中央。
    5. 用 href 設定連結。
    6. 裡面可以放置一個 <center> 來顯示按鈕文字。
    要注意的是 <v:roundrect> 內部不能再放一個 <v:roundrect> ,因為 <v:roundrect> 不支援嵌套。

    最後的語法結果如下:
    <!--[if mso]>
     <v:roundrect
    	xmlns:v="urn:schemas-microsoft-com:vml"
    	xmlns:w="urn:schemas-microsoft-com:office:word"
    	style="v-text-anchor:middle;position:absolute;height:64px;width:520px;"
    	arcsize="10%"
    	strokecolor="#f4f4f4"
    	fillcolor="#f4f4f4">
     </v:roundrect>
     <v:roundrect
    		xmlns:v="urn:schemas-microsoft-com:vml"
    		xmlns:w="urn:schemas-microsoft-com:office:word"
    		href="https://xxx.xxx.xxx"
    		style="v-text-anchor:middle;position:absolute;top:12;left:180;height:40px;width:160px;"
    		arcsize="10%"
    		strokecolor="#000000"
    		fillcolor="#000000">
    		<w:anchorlock/>
    		<center style="color:#ffffff;font-family:sans-serif;font-size:16px;">
    		  Click This Button
    		</center>
      </v:roundrect>
    <![endif]-->
    
    <!--[if !mso]><!-- -->
     <div style="border-radius: 10px; background-color: #f4f4f4; padding-top: 20px; padding-bottom: 20px; padding-right: 20px; padding-left: 20px;">
    	<a href="https://xxx.xxx.xxx" style="background-color: #000000; color: #ffffff; border-radius: 10px; border-top:10px solid #000000; border-bottom:10px solid #000000; border-right:20px solid #000000; border-left:20px solid #000000; font-size: 16px; text-decoration: none;">
    		Click This Button
    	</a>
     </div>
    <!--<![endif]-->
    

    參考資料:

    1. 向量標記語言 (VML) - Win32 apps | Microsoft Learn
    2. 使用預先定義的圖形 - Win32 apps | Microsoft Learn
    3. 從零開始建立一個 Email HTML 版型 | 電子豹部落格

    2021年12月20日 星期一

    用VBScript 讀本地Outlook信件檔案的收件者

    這邊紀錄下利用 VBscript 來
    1. 讀取本地端的 Outlook msg 檔案資訊,移如信件的收信者資訊。
    2. 讀取本地 Outlook 收件夾(或刪除的郵件、寄件備份等資料夾)裡的信件資訊。
    以下先上程式碼:
    <package>
    <job id=XXX>
    
    <script language="VBScript">
    
    parOutlookMsgFile("D:\testOutlookMail.msg")
    parseOutlookInboxFolder(6)
    
    '''''''''''''''''''''''''''''''''''''''''''''''''''''
    
    Sub parOutlookMsgFile(msgFilePath)
    Dim objOutlook
    'Dim objInBoxFolder
    'Dim objNameSpace
    
    Set objOutlook = CreateObject("Outlook.Application")
    'Set objNameSpace = objOutlook.GetNamespace("MAPI")
    'Set objInBoxFolder = objNameSpace.GetDefaultFolder(6)
    
    Dim mail, recips, recip, email_single, pa
    Set mail = objOutlook.CreateItemFromTemplate(msgFilePath)
    Set recips = mail.Recipients
    
    For Each recip In recips
        Set pa = recip.PropertyAccessor
        email_single = pa.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x39FE001E")
                 
        WScript.Echo "Receiver Name: " & recip.Name & ", Receiver Email: " & email_single & ", type: " & recip.Type
    Next
    End Sub
    
    '''''''''''''''''''''''''''''''''''''''''''''''''''''
    Sub parseOutlookInboxFolder(inboxFolderType)
    'inboxFolderType:
    ' received mail inbox: 6
    ' deleted mail inbox : 3
    ' sent mail inbox : 5
    Dim objOutlook, objInBoxFolder, objNameSpace, objMailItems
    
    Set objOutlook = CreateObject("Outlook.Application")
    Set objNameSpace = objOutlook.GetNamespace("MAPI")
    Set objInBoxFolder = objNameSpace.GetDefaultFolder(inboxFolderType)
    
    Set objMailItems = objInBoxFolder.Items
    Dim i 
    i = 1
    Dim totalMailCount
    totalMailCount = objMailItems.count
    While i <= totalMailCount
    		Set objMail = objMailItems.Item(i)
    		WScript.Echo objMail.Subject
    		i = i + 1
    Wend
    
    End Sub
    
    </script>
    
    </job>
    </package>

    說明:
    程式碼中有兩個函式,分別是用來讀取單一 Msg 檔資訊的 parOutlookMsgFile()
    和 讀取 Outlook 收件夾(或刪除的郵件、寄件備份等資料夾)裡的信件資訊的 parseOutlookInboxFolder()。

    在 parOutlookMsgFile() 中,recip.Type 可能有 1 或 2 兩種值, 
    Type = 1 代表一般收件者,
    Type = 2 代表 cc 副本的收件者。

    parseOutlookInboxFolder() 函式可以接收收件夾 type (OlDefaultFolders 形別) 的值,
    其中 6 代表收件夾,
    3 代表刪除的郵件,

    2020年7月13日 星期一

    使用 JACOB Java-COM Bridge 操作 Microsoft Outlook

    Java 無法直接執行作業系統的部份函式庫,
    例如無法直接執行並操作如 Microsoft Outlook, Word, Excel 等跟作業系統綁的較緊的軟體,
    如果真得要使用 Java 來操作,
    則必須要使用例如 JNI 或 Java-COM Bridge 等技術 (例如 JACOB, Jawin 等)來實現。

    Java Outlook Connector 是一個提供 Java 控制本地端 Outlook 的 Java Lib,
    將底層的 JNO、 COM、MAPI 等複雜的實現進行了封裝,
    不過只有試用版 (每次執行都會跳出建議購買的視窗) 及付費版。

    這裡要介紹的是另一個方案, JACOB,
    為一個免費的 Java-COM Bridge ,
    這邊要演示的是使用 JACOB 這個 Java-COM Bridge 來操作 Outlook 的範例。
    要注意的是,這裡只是直接操作電腦上的 Outlook 軟體,
    並非使用 JavaMail 來連接 mail server,
    所以並不需要進行登入連接等動作,
    可以想成類似直接使用 VBscript 來控制 Outlook。

    首先先去 JACOB 官網下載所需的 JAR 檔和 DLL 檔,
    裡面有提供最新版放在 Sourceforge.net 上的檔案下載頁面連結
    下載完解壓縮後可以看到所需的 jacob.jar、jacob-1.19-x64.dll  (或著是 32 位元,依執行環境自行選擇),也有附上 Javadoc 的說明文檔。

    引入 jacob.jar、jacob-1.19-x64.dll  (例如設定環境變數 path、或放到System32 資料夾下、或放到程式同目錄等,
    可參考
    source of com.jacob.com.LibraryLoader
    System.loadLibrary() 的使用方法彙總
    ) 後,
    就可以開始撰寫程式。

    範例程式碼如下,
    其中讀取了 Outlook 的 INBOX 收件夾,
    找出特定 Subject 的信,並將信上的附件檔存到本地資料夾。

    package outlookReaderTest;
    
    import com.jacob.activeX.ActiveXComponent;
    import com.jacob.com.Dispatch;
    
    public class JacobTest {
    
     public static void main(String[] args) {
      // 指定 INBOX 收件夾,參考 https://docs.microsoft.com/zh-tw/office/vba/api/outlook.oldefaultfolders
      int olFolderInbox = 6; 
      ActiveXComponent outlookApplication = new ActiveXComponent("Outlook.Application");
      Dispatch outlookObject = outlookApplication.getObject();
      Dispatch myNamespace = Dispatch.call(outlookObject, "GetNamespace","MAPI").toDispatch();
      Dispatch myFolder = Dispatch.call(myNamespace,"GetDefaultFolder", Integer.valueOf(olFolderInbox)).toDispatch();
      Dispatch mails = Dispatch.get(myFolder, "Items").toDispatch();
      
      /*
       * 上述程式碼等同於 VBscript 的:
       * Dim outlookObject, myNamespace, myFolder, mails
       * Set outlookObject = CreateObject("Outlook.Application")
       * Set myNamespace = outlookObject.GetNamespace("MAPI")
       * Set myFolder = myNamespace.GetDefaultFolder(6)
       * Set mails = myFolder.Items
       * 下面的程式碼跟 VBscript 的對照可以此類推
       */
    
      int count = Dispatch.call(mails, "Count").getInt();
      System.out.println("Total Mail Count: " + count);
      for (int i = 1; i <= count; i++)
      {
       Dispatch mail = Dispatch.call(mails, "Item", Integer.valueOf(i)).toDispatch();
       String subject = Dispatch.call(mail, "Subject").getString();
     
       if ("This is a test mail".equals(subject)) {
        System.out.println(i);
        System.out.println(subject);
        
        String saveFolderPath = "D:\\tempTestFoler";
        
        //Save attachments
        Dispatch attachs = Dispatch.call(mail, "Attachments").toDispatch();
        int attachCount = Dispatch.call(attachs, "Count").getInt();
        System.out.println("attachCount: " + attachCount);
        for (int j = 1; j <= attachCount; j++) {
         Dispatch attach = Dispatch.call(attachs, "Item", Integer.valueOf(j)).toDispatch();
         String attachName = Dispatch.call(attach, "FileName").getString();     
         Dispatch.call(attach, "SaveAsFile", saveFolderPath + "\\" + attachName);
         System.out.println(attachName + " -- saved");
         //Dispatch.call(mail, "Delete");
        }
       }
      }
     }
    
    }
    
    




    參考:

    1. Open source java library to read outlook emails, calendar etc
    2. 【JACOB】Java-COM Bridge 介紹
    3. COM - Microsoft物件導向開發架構的基礎
    4. Java Outlook Connector