/*本練習用於工作視窗(window)之螢幕上顯示大小寫英文字母,繁簡漢字,其他文字,數字及符號所組成的字串

並可使該字串能針對視窗大小來手動, 或用較精準的字體尺寸計算方式以進行自動換行

對於非漢字的其他語系文字部分(P.S. 本例使用英文與西里爾字母做為測試), 為免造成辨識錯誤

故採整個字詞跳行, 而非使用斷字之連字符號(-,連字號/減號(U+002D/0x002D))的方式

作業環境: Windows XP/Vista/Win7

C++編譯工具: Code::Blocks 12.11

函式庫: SDL(Simple DirectMedia Layer)及SDL_ttf

編寫者: 永吉

編寫日期: 20130724(Wed.)

先決條件:

1. 視窗須夠大

2. 字串內的所有字數不能超過字串顯示之範圍空間, 否則要有可進入下一頁或字幕捲動的設計(P.S. 本例皆無此設計)

3. 在編寫程式時, 讓使用者可在字串中自由選擇適當的位置預設強制跳行代號(連續兩個重音符號"`"(0x0060)),

使程式執行時遇到該代號便進行跳行

*/

//寫入相關的標頭檔

#include "SDL/SDL.h"

#include "SDL/SDL_ttf.h"

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

//工作視窗之螢幕尺寸及像素位元比之設定

const int SCREEN_WIDTH = 1000;

const int SCREEN_HEIGHT = 650;

const int SCREEN_BPP = 32;

//宣告畫面變數

SDL_Surface *MyMessage = NULL;//文字訊息

SDL_Surface *MyScreen = NULL;//螢幕

//宣告事件結構變數

SDL_Event MyEvent;

//宣告字體變數

TTF_Font *MyFont = NULL;

/*宣告字色變數

R255 G255 B255為白色, R0 G0 B0為黑色*/

SDL_Color MyTextColor = { 255, 255, 255 };//此處使用白色

//讓字串顯示於螢幕上

void ApplySurface( int x, int y, SDL_Surface* Source, SDL_Surface* Destination, SDL_Rect* Clip = NULL )

{

    //宣告字串顯示於螢幕的起始座標變數

    SDL_Rect Offset;

    //取得位置座標

    Offset.x = x;

    Offset.y = y;

    //張貼字串

    SDL_BlitSurface( Source, Clip, Destination, &Offset );

}

bool Init()//初始化之功能確認

{

    //SDL函式庫之初始化

    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )

    {

        return false;

    }

    //設定螢幕

    MyScreen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

    //螢幕設定出錯之跳出機制

    if( MyScreen == NULL )

    {

        return false;

    }

    //SDL_ttf之初始化

    if( TTF_Init() == -1 )

    {

        return false;

    }

    //顯示工作視窗的標題

    SDL_WM_SetCaption( "My String Test", NULL );

    //所有設定正常之回報機制

    return true;

}

bool LoadMyFiles( int MyFontSize )//載入字體檔

{

    /*開啟ttf字體檔與設定字體大小

    目前尚未取得本字體檔的版權, 故僅能進行測試使用*/

    MyFont = TTF_OpenFont( "C:/Windows/Fonts/kaiu.ttf", MyFontSize);

    //標楷體, 該檔位於C:/Windows/Fonts資料夾內, 理想可視字體尺寸參數(ptsize)建議設為18以上

    //未能正確開啟ttf字體檔之跳出機制

    if( MyFont == NULL )

    {

        return false;

    }

    //所有設定正常之回報機制

    return true;

}

int SAKL(int MyStringX, int MyStringY, int MyFontSize, wchar_t MyString[])//字串長度自動調整段落函式

{

    //載入字體相關檔案

    if( LoadMyFiles(MyFontSize) == false )

    {

        return 1;

    }

    //計算一個文字的寬度, 做為設定邊框間距的基準數值

    wchar_t MyWordSize[]=L"佛";//選用一測試文字, 以漢字為基準

    int MySL = wcslen(MyWordSize);//計算字串總共的字數長度, 作為迴圈參數使用

    char MyDTH[20];//宣告一個字元變數, 用作將數值轉換成字元

    Uint16 MyPM[MySL];//宣告一個十六位元無記號數值變數, 用作存放字串的十六進制數值

    //進行資料內容之轉換

    for (int i = 0; i < MySL; i++)

        {

         itoa(MyWordSize[i],MyDTH,16);//將變數內的十進制數值轉換成十六進制的字元, 並儲存於字元變數

         MyPM[i]= strtol(MyDTH,NULL,16);//將變數內的字元變成十六進制的數值, 並儲存於變數

        }

    MyPM[MySL]={0};//讓字串的結尾加上結束符號, 如此可避免列印出字串以外的無用資料

    MyMessage = TTF_RenderUNICODE_Solid( MyFont, MyPM, MyTextColor );//顯示跳行前的文字

    int MyStringLengh = wcslen(MyString),

        PSAreaWidth = SCREEN_WIDTH - (MyMessage -> w + MyStringX),//顯示字串區域之寬度, 單位:pixel

        SCFlag = 0;//字串內字元顯示指標之數值變數

    char DecToHex[20];//宣告一個字元變數, 用作將數值轉換成字元

    Uint16 PrintMyString[MyStringLengh];//宣告一個十六位元無記號數值變數, 用作存放字串的十六進制數值

    for (int i = 0; i < MyStringLengh; i++)

         {

          itoa(MyString[i],DecToHex,16);//將變數MyString內的十進制數值轉換成十六進制的字元, 並儲存於字元變數DecToHex

          PrintMyString[SCFlag]= strtol(DecToHex,NULL,16);//將變數DecToHex內的字元變成十六進制的數值, 並儲存於變數PrintMyString

          PrintMyString[SCFlag+1]= {0};

          MyMessage = TTF_RenderUNICODE_Solid( MyFont, PrintMyString, MyTextColor );

          if (PrintMyString[SCFlag] == 0x0060 && PrintMyString[SCFlag-1] == 0x0060)

            {

              if (SCFlag == 1)//避免形成空字串而造成執行錯誤

              {

                  PrintMyString[SCFlag-1]= {0x0020};//空格

                  PrintMyString[SCFlag]= {0};

              }

              else PrintMyString[SCFlag-1]= {0};

              MyMessage = TTF_RenderUNICODE_Solid( MyFont, PrintMyString, MyTextColor );

              ApplySurface( MyStringX, MyStringY, MyMessage, MyScreen );//在螢幕開始顯示字串

              MyStringY += MyMessage -> h;//跳行

              SCFlag = -1;

            }

          if (MyMessage -> w >= PSAreaWidth && PrintMyString[SCFlag] != 0x0060)//判斷因字數過長而必須跳行時

              {

                  /*判斷該字元是否為數字, 符號@(編碼: 0x0040), 大寫或小寫英文字母, 西里爾字母

                  Ref. https://zh.wikipedia.org/wiki/Unicode%E5%AD%97%E7%AC%A6%E5%88%97%E8%A1%A8*/

                  bool NH = false;//非漢字判斷變數

                  //忽略字母數目超過25的英文單字

                  int Ri = i,

                      RS = SCFlag;

                  //此處進行完整字詞的跳行處理

                  while (PrintMyString[SCFlag] == 0x0027//引號

                         || PrintMyString[SCFlag] >= 0x0030 && PrintMyString[SCFlag] <= 0x003F //數字

                         || PrintMyString[SCFlag] >= 0x0040 && PrintMyString[SCFlag] <= 0x005A //符號@與大寫英文字母

                         || PrintMyString[SCFlag] >= 0x0061 && PrintMyString[SCFlag] <= 0x007A //小寫英文字母

                         || PrintMyString[SCFlag] >= 0x0400 && PrintMyString[SCFlag] <= 0x04FF) //西里克字母

                  {

                     SCFlag --;

                     i --;

                     NH = true;

                  }

                  if (NH == true && (Ri-i) < 26)//以字母長度不超過25個為限 如此可避免程式無法執行

                  {

                      PrintMyString[SCFlag+1]= {0};

                      NH = false;

                  }

                  else //若字母長度超過25時 就不予以進行整字詞跳行處理

                    {

                        i = Ri-1;

                        PrintMyString[RS] = 0;

                        NH = false;

                    }

                  MyMessage = TTF_RenderUNICODE_Solid( MyFont, PrintMyString, MyTextColor );

                  ApplySurface( MyStringX, MyStringY, MyMessage, MyScreen );

                  MyStringY += MyMessage -> h;

                  SCFlag = 0;

              }

          else //顯示出字串最後不滿一行的所有字元

          {

              if (i == MyStringLengh-1)

              {

                  MyMessage = TTF_RenderUNICODE_Solid( MyFont, PrintMyString, MyTextColor );

                  ApplySurface( MyStringX, MyStringY, MyMessage, MyScreen );

              }

              else SCFlag ++; //若沒有滿足任何跳行或顯示文字條件時

          }

       }

}

void CleanUp()//清空或關閉所佔用之資源

{

    //清除顯示於螢幕上的內容

    SDL_FreeSurface( MyMessage );

    //關閉所開啟之ttf字體檔

    TTF_CloseFont( MyFont );

    //離開SDL_ttf

    TTF_Quit();

    //離開SDL

    SDL_Quit();

}

int main( int argc, char* args[] )

{

    //結束程式

    bool Quit = false;

    //執行初始化之功能確認

    if( Init() == false )

    {

        return 1;

    }

    /*輸入欲於螢幕上顯示字串, 此處的寬字元變數MyString會儲存該字串的十進制數值

    在Code::Blocks編輯環境裡, 可利用backslash(反斜線:\)來做為斷句的符號, 之後再按下Enter,

    如此以利編輯字串時的閱讀, 且此斷句符號不會在螢幕出現, 同時也不會對字串產生實質跳行效果

    以下字串如編輯正常則為藍色文字, 不正常則出現黑色文字

    下列有兩個測試方法, 可擇一執行之*/

//測試法一-起點:

    SAKL(10, 10, 26, L"雜阿含經卷第一       宋天竺三藏求那跋陀羅譯``\

(Ref. http://www.cbeta.org/result/normal/T02/0099_001.htm)``\

如是我聞:一時,佛住舍衛國祇樹給孤獨園。爾時,世尊告諸比丘:「當觀色無常。\

如是觀者,則為正觀。‧‧‧(餘略)``\

Sayukta-āgama(Miscellaneous Agama Sutra) Discourses 1       Bhikkhu Anālayo``\

(Ref. http://www.ddbc.edu.tw/zh/downloads/download_document.html?gid=3751)``\

Thus have I heard. At one time the Buddha was staying at Sāvatthī in Jeta's Grove, Anāthapiṇḍika's \

Park. At that time the Blessed One said to the monks: “You should contemplate form as impermanent. \

One who contemplates like this has right insight. ...(others to omit)````\

Hello, 我是`台灣人永吉(Цолмoн), 爱与和平!! \

poettw人間學社(poettw's Society of World)E-mail: poettw2010@gmail.com``\

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

//測試法一-終點

/*

//測試法二-起點:

    wchar_t MyString[] = L"雜阿含經卷第一       宋天竺三藏求那跋陀羅譯``\

(Ref. http://www.cbeta.org/result/normal/T02/0099_001.htm)``\

如是我聞:一時,佛住舍衛國祇樹給孤獨園。爾時,世尊告諸比丘:「當觀色無常。\

如是觀者,則為正觀。‧‧‧(餘略)``\

Sayukta-āgama(Miscellaneous Agama Sutra) Discourses 1       Bhikkhu Anālayo``\

(Ref. http://www.ddbc.edu.tw/zh/downloads/download_document.html?gid=3751)``\

Thus have I heard. At one time the Buddha was staying at Sāvatthī in Jeta's Grove, Anāthapiṇḍika's \

Park. At that time the Blessed One said to the monks: “You should contemplate form as impermanent. \

One who contemplates like this has right insight. ...(others to omit)````\

Hello, 我是`台灣人永吉(Цолмoн), 爱与和平!! \

poettw人間學社(poettw's Society of World)E-mail:poettw2010@gmail.com``\

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

    //讓字串在下列三種不同字體大小(14,15,18)及顏色的變化下顯示

    MyTextColor = { 86, 200, 240 };//此處使用藍色

    SAKL(10, 10, 14, MyString);

    MyTextColor = { 0, 255, 0 };//此處使用綠色

    SAKL(10, 190, 15, MyString);

    MyTextColor = { 255, 0, 0 };//此處使用紅色

    SAKL(10, 400, 18, MyString);

//測試法二-終點

*/

    //更新螢幕畫面

    if( SDL_Flip( MyScreen ) == -1 )

    {

        return 1;

    }

    //使用者關閉執行程式

    while( Quit == false )

    {

        //於事件功能運作狀態下

        while( SDL_PollEvent( &MyEvent ) )

        {

            //當使用者執行關閉視窗之動作時

            if( MyEvent.type == SDL_QUIT )

            {

                //結束程式

                Quit = true;

            }

        }

    }

    //清空或關閉所有已開啟的資源

    CleanUp();

    return 0;

}