2017年7月28日 星期五

最恐怖的鬼故事


就在即將進入農曆七月前,我聽到一個最可怕的鬼故事。

小明跑到一家書店,問老闆說,你們這有沒有最可怕的故事書。老闆推推眼鏡,然後冷冷的回答說,有。於是從書架上取下一本書,並告訴小明,這本故事書很特別,每個章節都很恐怖,要價 1000元,你要買嗎?

小明沒有猶豫很久,就跟老闆說,我要買。當老闆把書交給小明之際,低聲的對小明說,有一件事你一定要特別注意,就是「千萬不要翻到最後一頁,千萬要記住喔」。

2017年7月26日 星期三

調整Bootstrap 裡的 input-group 高度

Bootstrap 裡使用  input-group ,可以很輕易的讓使用者建構出鮮明清爽的輸入項目。

但有發現嗎?在備註二,如果內容的文字一多,就會出現這種爆表的現象。

目前找到一個有效的解決方式,就是透過調整 CSS ,針對 .input-group-btn, .input-group .form-control 這些 class 設定  height: auto
但單單僅這樣設定,反而造成其他尚未有 input 內容的畫面變得很乾扁。

所以再針對每一個項目設定了最小高度,例如:min-height: 30px;

經由這樣的設定後,整體呈現總算有及格了。


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default5.aspx.cs" Inherits="Default5" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="http://getbootstrap.com/favicon.ico">    
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
    
    <style>
        .input-group-addon {
            /*設定每個 input-group-addon 都有固定寬度 */
            min-width:180px;
        }

        .input-group{
            padding-bottom:2px;
        }

        /*自動根據內容調整高度,同時設定最小高度*/
        .input-group-btn, .input-group .form-control
        {
            height: auto;
            min-height: 30px;
        }
    </style>

</head>
<body>
    <form id="form1" runat="server">
    <div class="container">

        <div>
        &nbsp;
        </div>

        <div class="row">
            <div class="col-md-6">
                <div class="input-group">
                    <span class="input-group-addon">學生姓名</span>
                        <asp:Label id="lb_學生姓名" runat="server"  CssClass="form-control" Text="張小明" ></asp:Label> 
                </div>
            </div>
            <div class="col-md-6">
                <div class="input-group">
                    <span class="input-group-addon">年級班級</span>
                        <asp:Label id="lb_班級" runat="server"  CssClass="form-control" Text="五年一班" ></asp:Label> 
                </div>
            </div>
        </div>

        <div class="row">
            <div class="col-md-6">
                <div class="input-group">
                    <span class="input-group-addon">備註一</span>
                        <asp:Label id="lb_備註一" runat="server"  CssClass="form-control" Text="" ></asp:Label> 
                </div>
            </div>
            <div class="col-md-6">
                <div class="input-group">
                    <span class="input-group-addon">備註二</span>
                        <asp:Label id="lb_備註二" runat="server"  CssClass="form-control" Text="2014年校慶運動會跑步第一名<br>2015年校慶運動會跑步第二名<br>2016年全國運動會跑步第一名" ></asp:Label> 
                </div>
            </div>
        </div>
    </div>
    </form>
</body>
</html>



參考:.input-group-lg causes textarea's height to shrink to a single line

2016年12月1日 星期四

SSRS 報表使用上下標數字

是否有比較好的方法,在 Reporting Service 報表裡面使用上標(superscripts)、下標(subscripts)數字呢?

這個看似簡單的需求,目前的 Reporting Service 報表設計工具,似乎沒有很直覺簡單的處理方式。有人會建議,那就放圖片吧,這可行,但是圖片大小可能會因為報表的放大、縮小而失真,對工程師而言,要找到解析度剛好的圖片,其實不是一件容易的事。剩下比較常見的,就是透過 Unicode 的方式,取得上標或下標的數字,來完成我們的工作。

剛好最近的需求,是同時會用到數字的上標與下標,同時看到 SQLJason 裡的一篇文章,覺得很有價值。

他提出兩種方法。

第一種:是透過 chrw( ) 函數。把作者整理出來的圖表節錄如下,我們把 Superscript 的內容放到 chrw( ) 裡,就可以得到我們想要的上標了。



在報表裡,我們只需要再文字方塊的 Expression 裡面輸入:
="2"&chrw(178)

就可以得到 2 的平方。

※補充:20170120
如果還有其他想要加入的文字,可以透過 charmap.exe 打開字元對應表,並在其中找到想要的字符,此時要注意的有兩件事,第一是最上面的「字形」,要能夠正確顯示出內容,如果字型沒正確指定,則會出現亂碼。第二,是最下面的訊息框,它會顯示目前該字符的Unicode,但我們不能直接把它放到 chrw()裡面,因為他是 16 進位,所以要把它轉換成 10 進位的數字,可以到 convert 網頁把它轉換為 10 進位後,再放到 chrw()裡。



第二種:可以透過電腦的執行命令列,輸入 charmap.exe


就可以跳出字元對應表,這時,先在字型的下拉選項中挑選 Lucida Sans Unicode,接著你就可以在下方的字元清單中找到你想要的字,包含上標、下標的數字都有。


當你找到之後,就可以按選取,把他存放到「要複製的字元」,接著繼續挑,直到您滿意為止,最後再按「複製」。接著,就到報表設計工具,找到文字輸入方塊,點兩下後,直接用 Ctrl+V 貼上,這時,還要做一個動作,就是將文字輸入方塊的屬性裡的 FontFamily,也要設成:Lucida Sans Unicode。這樣,就可以正常顯示上下標文字了。



參考文章:
01.Subscripts and Superscripts in SSRS Reports

02.SSRS Reports – Displaying Superscripts and Subscripts

2016年11月29日 星期二

處理 ReportViewer 撐破版面的作法

在網頁上能夠即時產生出報表來瀏覽,其實還蠻方便的。程式開發者可以透過 ReportViewer 來完成這項功能。但是,如果發現要呈現的報表內容很長時,就會有頁面爆掉的風險。

舉例來說,我預期正常畫面應該長成下面這樣子。上面是報表內容,下面還有我的頁尾。


但是,結果卻是長成下面這樣子。


報表內容已經撐破了原先設定好的版面框架。這時,但有趣的,是 IE 瀏覽器會這樣,非 IE 的瀏覽器則不會。後來,在 ReportViewer 的設定裡,找到一個屬性:SizeToReportContent 。他預設是 false ,表示報表不會自動調整控制項來符合報表內容。所以,當我把他設成 True 之後,就可以解決版面被撐破的問題了。

2016年8月31日 星期三

曾被我們遺忘的台電

在 Cheers 雜誌讀到:

「回溯台灣的百年電力史,第一章可從1887年(清光緒13年)寫起。清朝台灣巡撫劉銘傳邀請江浙仕紳集資白銀五萬兩,於台北市東門外設立「興市公司」(建設公司),隔年裝置小型蒸氣燃煤發電機,在丹麥電氣技師協助下,才在台北城內點亮了「台灣第一盞燈」。

接下來的幾個重要里程碑,包括1905年7月,台灣第一座水力發電所──龜山發電所完工,從此讓台北市區、艋舺及大稻埕地區都進入「有電時代」。而1934年(日據昭和9年)竣工的日月潭水力發電工程,更是推動「農業台灣」進入「工業台灣」的關鍵動力。日月潭水力發電廠是當時亞洲最大、世界第八的發電工程。光是日月潭第一發電所的發電量,即足以滿足1935年時,台灣電力株式會社的全系統實際尖峰負載,還整整多出38%的備載容量。
」

原來台灣電力的發展史,也是這麼的迷人。跨越清朝、日治、民國三朝,可真的稱的上是「三朝元老」了,同時也參雜了台灣百年來的政經情勢。拋開政客們偏頗的口號對立,坐下來好好欣賞這曾經有無數人犧牲奉獻所換得的產物,也是令人握緊拳頭、血淚交織啊!

2016年8月29日 星期一

使用 jQuery 完成 JSON 資料的傳遞

聽到要透過 JSON 來傳遞資料,心中不免冒出:那不是要寫很多行程式?不是還要引用 JSON 相關的 javascript 模組?不是還要... 總之,就是很煩就是了!

最近看了幾篇 JSON 相關的使用文章後,發現其實不困難,也還蠻簡潔的。或許有很多小細節的地方我還沒發現,但大致上目前會用到的功能,都已經可以達到了。

首先,要先建立一個模擬後端的程式,用來產生 JSON 資料,準備給前端使用,而內容就大概長得像下面這個樣子。


{
"title":"jquery 入門",
"price":"299",
"author":"paladin lee"
}


只是最後要回傳的時候,需將物件序列化成 JSON 格式再回傳,而也只需使用下面這方法就可以了。

System.Web.Script.Serialization.JavaScriptSerializer().Serialize(stringValue)

所以我的後端程式如下:

ser01.ashx

public void ProcessRequest (HttpContext context) {

    StringBuilder sb = new StringBuilder();
    sb.Append(@"{
    ""title"":""jquery 入門"",
    ""price"":""299"",
    ""author"":""paladin lee""
}
");

    //將 sb 物件序列化成JSON格式再回傳
    context.Response.Write(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(sb.ToString()));
}


接下來要處理的,就是前端了。透過  jQuery 的 getJSON() 方法,可以讓我們輕鬆的接收回傳的 JSON 資料。再透過 $.each() 來一一處理每個項目的內容。

 $.each(data2, function (property, value) {

   //Do something...

 });


從後端所序列化的內容可知,回傳的資料裡面三筆,以第一筆為例, property 是 "title",value 則是 "jquery 入門"。這裡要特別注意,因為回傳的 JSON 資料目前只是被視為字串,要透過 $.parseJSON() 將這 JSON 字串轉為 JSON 物件才能進一步被我們使用。

前端完整的測試程式則如下所示:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style>
        .tb  
        {
            border-collapse:collapse ;                  
        }
        
        .tb th
        {
            border:1px solid black;
            padding:5px;
        }
        
        .tb td
        {
            border:1px solid black;
            padding:5px;
        }
        
    </style>
    <script src="js/jquery-1.10.2.min.js" type="text/javascript"></script>
    <script>
        $(function () {
            
            $.getJSON("ser01.ashx", function (data) {

                var vTable = $("<table class='tb' border='1'></table>");
                var vItem01="";

                //convert JSON string, not an object
                var data2 = $.parseJSON(data);

                $.each(data2, function (property, value) {
                    vItem01 = vItem01 + "<tr><th>" + property + "</th><td>" + value + "</td></tr>";
                });                               

                $(vTable).append(vItem01);
                $(".div01").append(vTable);

            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">   
    <div class="div01">
    </div>
    </form>
</body>
</html>


而畫面結果就會長成這樣子:


這時,或許你可能會說,我其實想要的,是類似好幾本書的資訊,而不是單單某一本書的詳細內容ㄟ!這其實也不難,首先來產生後端的資料,其內容大概如下:
[
 {
 "title":"jquery 入門",
 "price":"299",
 "author":"paladin lee"
 },
 {
 "title":"html5 入門",
 "price":"199",
 "author":"Nikki"
 },
 {
 "title":"麻將",
 "price"":"99",
 "author"":"jason"
 }
]

同樣地,將他序列化後,就可以直接送出去了。

server02.ashx

public void ProcessRequest (HttpContext context) {
 StringBuilder sb = new StringBuilder();
 sb.Append(@"[
 {
 ""title"":""jquery 入門"",
 ""price"":""299"",
 ""author"":""paladin lee""
 },
 {
 ""title"":""html5 入門"",
 ""price"":""199"",
 ""author"":""Nikki""
 },
 {
 ""title"":""麻將"",
 ""price"":""99"",
 ""author"":""jason""
 }
 ]
 ");
 //將 sb 物件序列化成JSON格式再回傳
 context.Response.Write(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(sb.ToString()));
}


至於前端的部份,我們則是透過兩次的 $.each() 來把回傳的 JSON 資料解析出來。
$.each(data, function () { 
 //處理每一筆的資料
 $.each(this,function(property, value){
  //處理每一個欄位的資料
 });                    
});



完整前端程式如下:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style>
        .tb  
        {
            border-collapse:collapse ;                  
        }
        
        .tb th
        {
            border:1px solid black;
            padding:5px;
        }
        
        .tb td
        {
            border:1px solid black;
            padding:5px;
        }
        
    </style>
    <script src="js/jquery-1.10.2.min.js" type="text/javascript"></script>
    <script>
        $(function () {

            // test ser01
            $.getJSON("server02.ashx", function (data) {

                var vTable = $("<table class='tb' border='1'></table>");
                var vItem01 = "";

                //convert JSON string, not an object
                var data2 = $.parseJSON(data);

                //header
                vItem01 += "<tr><th>title</th><th>price</th><th>author</th></tr>";                               

                $.each(data2, function () { 
                    vItem01+="<tr>";
                    $.each(this,function(property, value){
                        vItem01+="<td>"+value+"</td>";
                    });
                    vItem01 += "</tr>";                    
                    
                });

                $(vTable).append(vItem01);
                $(".div01").append(vTable);

            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div class="div01">
    </div>
    </form>
</body>
</html>


希望透過以上簡單範例,會讓你對於 jQuery 與 JSON 資料傳遞會有更親切的感覺。

2016年8月26日 星期五

用Aspose替 PDF 加上底圖


如果一張獎狀的 PDF 檔,內容長得如上圖,會不會覺得哪裡怪怪的?沒錯,少了「框線」。

接下來想要透過 Aspose 工具,來把這張 PDF 檔加上框線。而這原理,可以簡單的想成把 PDF 檔案加上底圖。

首先,先找好一個有框的底圖。大概像下面這樣:

BackGround.png
接著,就可以透過 Aspose 的 Document 物件,來取得原先的 PDF 檔案。這時候的 Document ,可以視為 PDF 的代表。
Document pdfDocument = new Document("PDF來源");

一份 PDF 檔案,可能會有很多頁(Page),所以,透過 Page 物件,可以讓我們指定要控制第幾個頁面。而這裡的頁面標示,則是從 1 開始算起。
Page pdfPage = pdfDocument.Pages[1];

ImageStamp 則是接下來的主角,他就好比我們的橡皮圖章一樣,待會會蓋在每個頁面上。若用圖層的觀點來看,就有所謂的上下之分,這時,可以利用 ImageStamp 的 Background 屬性來決定,決定這個橡皮圖章的內容是要完全蓋過原先的內容,還是只是當個背景。如果需要微調 ImageStamp 的大小,則有 Width、Height 可以設定。
ImageStamp imgStamp = new ImageStamp(strStampFullPath);


完整程式可以簡述如下:
class PdfFunction
{
    public void AddBackGroundIMG(string PDFPath)
    {

        string setupFile = "BackGround.png";

        //取得執行路徑
        string strExecPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        //取得檔案的完整路徑
        string strStampFullPath = string.Format("{0}\\{1}", strExecPath, setupFile);

        //設定 license
        Aspose.Pdf.License license = new Aspose.Pdf.License();
        license.SetLicense(string.Format("{0}\\{1}",strExecPath , "Aspose.Total.lic"));


        //透過 Aspose 的 Document 物件,來取得原先的 PDF 檔案
        Document pdfDocument = new Document(PDFPath);
        Page pdfPage;
                       

        for (int i = 0; i < pdfDocument.Pages.Count; i++)
        {        
            //Aspose 是從 1 開始算的
            pdfPage = pdfDocument.Pages[i + 1];

            //取得外部圖片並指定為 ImageStamp 格式
            ImageStamp imgStamp = new ImageStamp(strStampFullPath);

            //將加入的圖片指定設為背景
            imgStamp.Background = true;

            //設定圖片長寬
            imgStamp.Width = 850;
            imgStamp.Height = 600;

            //將底圖加入到 PDF 頁面
            pdfPage.AddStamp(imgStamp);

        }


        // 產出新檔案
        pdfDocument.Save(string.Format("{0}\\{1}", strExecPath, "out.pdf"));

    }
}


要使用時,則是如下呼叫:
class Program
{
 static void Main(string[] args)
 {
  string setupFile = "award.pdf";

  //取得執行路徑
  string strExecPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
  //取得檔案的完整路徑
  string strStampFullPath = string.Format("{0}\\{1}", strExecPath, setupFile);

  PdfFunction oP = new PdfFunction();
  oP.AddBackGroundIMG(setupFile);
 }
}


最後,有框的獎狀就會像下面這樣子:




Ref:
01:how to insert a watermark image into existing .pdf