Hatena::ブログ(Diary)

ザリガニが見ていた...。 このページをアンテナに追加 RSSフィード

2009-02-24

シンタックスハイライトなHTMLに変換するオブジェクト指向AppleScript その3

...前の日からの続き

RTFオブジェクトを一般化しておく

  • リッチテキスト情報を取得するRTFオブジェクトは、今後も何かの役に立ちそうな気がする。
  • 現状はアクティブなアプリケーションの最前面のRTF情報決め打だが、
  • アプリケーションと書類の名前で指定できれば、もっと一般的に活用できそう。
  • そこで、今までクラスのまま利用していたRTFオブジェクトを、new()でインスタンス化することにした。
  • その時、特定のアプリケーションのRTF書類を指定するようにしてみた。(RTF's new(app_name, doc_name_or_num))

--??は、インライン改行(option-L)にすべて置き換える
--半角の¥は、半角の\にすべて置き換える

(*
RTFクラス
*)
--アプリケーション名と書類の名前あるいは番号を指定して、RTF情報にアクセスする
--  アプリケーション名が""の場合は、その時アクティブなアプリケーションになる
--  書類の名前が""の場合は、document 1(最前面のウィンドウの書類)になる
--利用例:
--  set RTF_OBJ to new("", "")
--  RTF_OBJ's everyText
--  RTF_OBJ's everyFont
--  RTF_OBJ's everySize
--  RTF_OBJ's everyColor
on new(app_name, doc_name_or_num)
  if app_name = "" then set app_name to frontmost_app()
  if doc_name_or_num = "" then set doc_name_or_num to 1
  
  script RTF_instance
    property parent : me
    --global everyText, everyFont, everySize, everyColor
    property everyText : missing value
    property everyFont : missing value
    property everySize : missing value
    property everyColor : missing value
    
    --初期化
    on initalize()
      --validate(検証)の順序大事
      validate_presence_of_document()
      parse()
      validate_rtf()
      validate_presence_of_text()
      me
    end initalize
    
    --リッチテキスト情報を取得する(都度、メソッド呼び出しで処理すると、とっても遅くなったので、変数に代入することに)
    on parse()
      try
        tell application app_name
          tell document doc_name_or_num's paragraph
            set everyText to (every attribute run)
            set everyFont to (every attribute run)'s font
            set everySize to (every attribute run)'s size
            set everyColor to (every attribute run)'s color
          end tell
        end tell
      end try
    end parse
    
    --documentの存在を検証
    on validate_presence_of_document()
      tell application app_name
        try
          if (count document) > 0 then return
        end try
        "ドキュメントがありません。"
        --display dialog result buttons "OK" default button "OK" giving up after 10
        display alert result giving up after 10
        error
      end tell
    end validate_presence_of_document
    
    --テキストの存在を検証
    on validate_presence_of_text()
      tell application app_name
        try
          if everyText's number > 0 then return
        end try
        "変換するテキストがありません。"
        --display dialog result buttons "OK" default button "OK" giving up after 10
        display alert result giving up after 10
        error
      end tell
    end validate_presence_of_text
    
    --変換できるリッチテキストかどうか検証
    on validate_rtf()
      tell application app_name
        try
          if everyText's number = everyFont's number and ??
            everyText's number = everySize's number and ??
            everyText's number = everyColor's number then return
        end try
        "リッチテキストでないため変換できません。"
        --display dialog result buttons "OK" default button "OK" giving up after 10
        display alert result giving up after 10
        error
      end tell
    end validate_rtf
  end script
  result's initalize()
end new

--最前面のアプリケーション名(拡張子なし)を取得する
on frontmost_app()
  --short name of (info for (path to frontmost application)) -- short name属性がない場合、missing valueが返ってくる
  --name of (path to frontmost application) --拡張子が付属してしまう。"Script Editor.app"
  tell application "System Events"
    set name_list to processes's name whose frontmost is true
    name_list's first item
  end tell
end frontmost_app

  • 上記コードを_rtf.scptとして、ユーザースクリプトフォルダに保存しておいた。

load scriptを活用した書き方(rtf_to_html.scpt)

  • 今、ユーザースクリプトフォルダ(~/Library/Scripts)には自分にとって便利なAppleScriptコードがストックされつつある状況。
  • さっき保存した_rtf.scptは、リッチテキスト情報を取得するクラス。
  • それから、_lib.scptは、テキスト置き換えなどの一般的によく使いそうなコード集。
  • これらのライブラリ的スクリプトを活用して、rtf_to_html.scptを書き直してみた。

--??は、インライン改行(option-L)にすべて置き換える
--半角の¥は、半角の/にすべて置き換える

property LIB : load script file ((path to scripts folder as text) & "_lib.scpt")
property RTF : load script file ((path to scripts folder as text) & "_rtf.scpt")
property FIND_TEXTS : {"\t", "&", "<", ">"} --"\t" = tab
property PUTS_TEXTS : {"  ", "&amp;", "&lt;", "&gt;"}

set RTF_OBJ to RTF's new("", "")

set html to ""
repeat with i from 1 to RTF_OBJ's everyText's number
  set line_text to RTF_OBJ's everyText's item i
  set line_font to RTF_OBJ's everyFont's item i
  set line_size to RTF_OBJ's everySize's item i
  set line_color to RTF_OBJ's everyColor's item i
  
  repeat with j from 1 to line_text's number
    set aText to line_text's item j
    set aFont to line_font's item j
    set aSize to line_size's item j
    set aColor to line_color's item j
    
    set html to html & text_or_tag(aText, aFont, aSize, aColor)
  end repeat
end repeat

set the clipboard to tag("pre" & return, html, "")



(*
htmlに変換するハンドラ
*)
--見えない文字はそのまま、見える文字はタグで囲って返す
on text_or_tag(aText, aFont, aSize, aColor)
  --if  reg("/^[\\t\\s\\r]+$/", originalText) then--処理が遅過ぎ
  if is_invisible(aText) then
    content_text(aText)
  else
    tag("span", content_text(aText), {"style", css(aFont, aSize, aColor)})
  end if
end text_or_tag

--見えない文字の連続かどうか真偽値で返す(見えない:true)
on is_invisible(str)
  str's item 1 & LIB's replace(str, str's item 1, "")
  result is in {tab, space, return}
end is_invisible

--タグで囲って返す
--利用例:
--  tag("span", "ABC", {{"class","AppleScript"}, {"style","color:rgb(0,0,0);"}})
--  <span class="AppleScript" style="color:rgb(0,0,0);">ABC</span>
--ペアリストが一つだけの場合は下記でもOK
--利用例:
--  tag("span", "ABC", {"style","color:rgb(0,0,0);"})
--    <span style="color:rgb(0,0,0);">ABC</span>
--  tag("pre", "ABC", "")
--    <pre>ABC</pre>
--  tag("pre"&return, "ABC", "")
--    <pre>
--    ABC
--    </pre>
--    
on tag(tag_name, str, attr_pair_list)
  if tag_name's item -1 is in {return, "\n"} then
    return tag(tag_name's items 1 thru -2 as text, return & str & return, attr_pair_list) & return
  end if
  if (count attr_pair_list)  1 then
    "<" & tag_name & attr_text(double_list_from(attr_pair_list)) & ">" & str & "</" & tag_name & ">"
  else
    "<" & tag_name & ">" & str & "</" & tag_name & ">"
  end if
end tag

--二重のリストに補正する
--本来、リストの中にリストを指定する想定なのだが...
--  {{"class","AppleScript"}, {"style","color:rgb(0,0,0);"}}
--リスト一つだけでは以下のように書いてしまいがち
--  {"class","AppleScript"}
--そのような状況で、本来の二重リストに修正する
--  {{"class","AppleScript"}}
on double_list_from(aList)
  if aList's item 1's class is list then
    aList
  else
    {aList}
  end if
end double_list_from

--ペアリストから属性を指定するテキストを返す
--ペアリストとは{キー,値}のリストから構成されるリストを想定している
--{{"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}}
--利用例:
--  attr_text({{"class","AppleScript"}, {"style","color:rgb(0,0,0);"}})
--  " class=\"AppleScript\" style=\"color:rgb(0,0,0);\""
on attr_text(pair_list)
  set aText to ""
  repeat with pair in pair_list
    set aText to aText & space & pair's item 1 & "=\"" & pair's item 2 & "\""
  end repeat
end attr_text

--必要な置き換えをしたテキストを返す
on content_text(str)
  LIB's every_replace(str, FIND_TEXTS, PUTS_TEXTS)
end content_text

--インラインスタイルを返す
on css(aFont, aSize, aColor)
  --css_color(aColor) & css_font_size(aSize) & css_font_weight(aFont)
  css_color(aColor) & css_font_size(aSize) & css_font_family(aFont)
end css

--フォントの色の設定コードを返す color:rgb(255,255,255);
on css_color(aColor)
  "color:" & rgb_color(aColor) & ";"
end css_color

--フォントサイズの設定コードを返す font-size:12px;
on css_font_size(aSize)
  "font-size:" & aSize & "px;"
end css_font_size

--フォントの太さの設定コードを返す font-weight:bold;
on css_font_weight(aFont)
  if "bold" is in aFont then
    "font-weight:bold;"
  end if
end css_font_weight

--フォントの種類の設定コードを返す font-family:Osaka
on css_font_family(aFont)
  "font-family:" & aFont
end css_font_family

--RGB指定の文字列を返す
--利用例:
--rgb_color({65535, 65535, 65535})
--  結果:"rgb(255,255,255)"
on rgb_color(color_list)
  set R to (color_list's item 1) div 256
  set G to (color_list's item 2) div 256
  set B to (color_list's item 3) div 256
  "rgb(" & R & "," & G & "," & B & ")"
end rgb_color

  • 余分なコードが視界から消えて、リッチテキストからHTMLに変換する処理の主要な部分だけになり、とても読み易くなった。

次の日に続く...

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/zariganitosh/20090224/1235460563