処理コードを変更する 

前ページの調査で「path」コードの処理方法に裏打ちが出来たので、いちから処理コードを見直しました。

 

下は、このツールの処理エンジンが対象とする「数値塊」です。「数値塊」は私が勝手に呼んでいる名ですが、「path」を構成する「半角スペース」か「カンマ」で区切られた文字列の事です。 下の青枠は「path」コードに並んだ「数値塊」の例で、短いものからかなり長いものまで、様々です。

 

 

「ver. 0.1」の処理コードは、「-」を含む「数値塊」を除外していました。 上の例は「-」が良く出て来る例で、処理できる「数値塊」がとても少なくなります。

 

また、「数値塊」を最初に「アルファベット文字」で分解して、「nnnCnnn」の様に2個の数に分解できる場合のみを、処理対象にしていました。

 

これは、本当は処理できる「数値塊」を、かなり取りこぼす処理コードと言えます。 とはいえ、自作画像を処理する場合は、余り「-」も混ざらず、使える場合が多かったのですが。

 

で、「Font Awesome」の「SVG」や、ネット上のフリーアイコンの「SVG」を色々と入手して調べると、「-」が盛大に混ざるものや、「数値塊」がとても長いものなどが多く、「ver. 0.1」は全く使えない場合が多いと感じました。

 

そこで、前ページの知識を基に、色々な「SVG」を扱える処理方法を考えました。

 

 

 

根本的に異なるアプローチ 

「path」(正しくはd属性)の文字列を最初から「1文字ずつ」の配列に分解して、その配列の先頭から一つずつ判定をします。

 

下はその処理のダイアグラムで、「path」の最初の部分の処理の例です。 実際は、pathの文字数だけ右にダイアグラムが続きます。

 

A:アファベット 「-」マイナス 「 」半角スペース

n:数文字 または「.」ドット

count:処理ループで、nを処理した場合のカウント値(出力時ごとにリセット)

f(引数):countが0でない場合にAが出現したら処理関数 f() を動作させる

 

index    0 1 2 3 4 5 6 7 8 9
文字   A n n n A n n n A n
count   0   1   2   3   1 2   1
f(引数)           3/4       3/8  
数値化      ○  ○  ○     ○  ○  ○     ○ 
出力   A      整数 A      整数 A  

 

ループで「アルファベット」「-」「半角スペース」に出会った所で、それまでに「数値化の対象と判定した文字数=count」と、現在の「インデックス」を、処理関数に送ります。(インデックスは「path」の先頭からの位置情報)

 

処理関数は、「インデックス」をcount数だけ遡った場所から、「path」の数文字を順にcount個繋ぎ数値化します。 それを四捨五入した整数を出力し、現在の区切り文字Aを、その後方に出力します。(橙色が出力)

 

但し、数値化の対象文字に「.」が2個以上含まれている場合は、数文字を繋ぐだけで処理をせず、後方に現在の区切り文字を追加して出力します。

 

以上の様に、「path」の全文字を先頭から順に処理を繰り返して別の配列に出力し、最後にそれらを繋いで「path」の元の「d属性」と入れ替えます。

 

なお、「path」の文字列は必ず両端に「アルファベット」が付くので、「path」の末尾では、末尾の「アルファベット」を出力して処理が終了します。

 

以下が、その処理部のコードです。

 

function arrange_i_code(path_str){ // path全体の処理
    let c_count=0; // 数として処理した文字数
    let i_code=[]; // 全pathを1文字ずつにした配列
    let i_code_new=[]; // 処理後の数と文字を格納する配列

    i_code=path_str.split(''); // pathの全体を文字1個ずつに分解
    for(let k=0; k<i_code.length; k++){
        if(i_code[k].match(/[A-Za-z]| |-/g)){
            if(c_count==0){
                i_code_new.push(i_code[k]); }
            else{
                i_code_new.push(arr_i_code(c_count, k));
                c_count=0; }}
        else{ // 数字または「.」の場合
            c_count+=1; }}

    return i_code_new.join(''); // 処理した文字列を繋いで出力


    function arr_i_code(Count, K){ // 処理関数 
        let tmp=[];
        let tmp_num; // 再構成した数値(小数を含む)
        let tmp_round; // 四捨五入した数値
        for(let i=0; i<Count; i++){
            tmp.push(i_code[K-Count+i]); }

        if(count_dot(tmp.join(''))){ //「.」が1個以内
            tmp_num=parseFloat(tmp.join(''));
            tmp_round=Math.round(tmp_num);
            return tmp_round.toString()+i_code[K]; }
        else{ //「.」が2個以上
            return tmp.join('')+i_code[K]; }

        function count_dot(str){
            let count=0;
            for (let i=0; i<str.length; i++){
                if(str[i]=="."){
                    count++; }}
            if(count<2){
                return true; }
            else{
                return false; }}} // arr_i_code

} // arrange_i_code()

 

 

このコードに至る前に、2個目の「.」を区切りとして処理するコードを作りましたが、テストすると上手く行きません。「nnn.nnn.nnn」形式の処理は、原理的には最初の「nnn.nnn」を小数として処理し、後方の「.nnn」を「0.nnn」として処理すれば良いはずですが、手作業で処理してもデリケートで、ここで崩れてしまいます。

 

後方の「.nnn」だけ小数2位で丸める様な処理が必要な気がしますが、今回これは処理をしない方式にしました。「.」が3個以上繋がる例は見た事がなく、2個も出現率は低いので、今後の課題にしておきます。

 

以上の処理コードは、「ver. 0.1」よりもかなり進んだコードになったと思います。

 

 

 

複数の「path」を処理可能にしました 

「ver. 0.1」は単一の「path」しか処理できなかったのですが、このバージョンは複数の「path」構成のSVGも処理可能です。

 

このバージョンは、対象「SVG」のクローンを作り、その「path」を直接処理する様にしています。 この方法は、処理後に「SVG」の再構成が不要で、元のサイズ属性や「viewBox」属性をコピーする操作も省けて、最善策です。 今後は、不要な属性を削除する機能も追加する予定です。

 

 

 

「半角スペース」「カンマ」の両タイプに対応 

「path」の区切り文字をどちらで処理するか、選択が必要です。 実装の手間を簡単にするためにメニューを作らず、「半角スペース」は「F1」、「カンマ」は「F2」で処理する様にしました。 不適当な処理をした場合は、押し直すだけで訂正できます。

 

また、各種テストで「カンマ」区切りは扱い難いので、「F3」を押すと「カンマ」区切りのSVGを「半角スペース」区切りのSVGに変更する機能を追加しました。 この場合は短縮処理をせず、画像劣化は全く生じません。 これを利用して「F1」「F2」と「F3」を交互に押して、微細な劣化を評価できます。

 

また、処理後のSVGのコードをコピーし易くするため、「id」や「class」名を付加しないコードにしました。 処理実行ごとに、最初の「SVG」以外を削除してクローンを生成し、編集画面上に「SVG」は2個以上にならない様にしています。「F1」「F2」を押すたびに、処理した2個目の「SVG」が更新される仕様です。

 

 

 

アイコンのSVGを作る手順 

◎ アイコン用のSVGを作る場合は、「256×256 dpt」の2値(モノクロ)か、3値(補助色部あり)の元絵が適当と思います。 これは、手持ちのグラフィックアプリで行います。

 

◎ 以下のサイトで「png→svg」変換を行い、処理後のSVG画像(ファイル)をダウンロードして入手します。

 

 

◎ SVG画像をメモ帳で開き、SVGのソースコード(テキスト)を得ます。

 

 

 

簡略化処理 

◎「Simple SVG」をONにした上で「ブログ編集画面」を開きます。

 

◎ 処理するSVG画像のソースコードを「HTML表示」画面にペーストします。 これは、SVG画像のみの記事を作る操作です。

 

◎ アメーバのHTML画面は「title」が禁止タグなので、含まれる場合は削除します。

 

 

◎「通常表示」に戻ると、編集枠内に処理前のSVG画像が表示されます。

 

 

◎「path」が「半角スペース区切り」の場合は「F1」キーを押すと、記事上のSVG画像を処理し、処理後のSVG画像が「======」の区切り線を挟んで追加されます。

 

◎「path」が「カンマ区切り」の場合は「F2」キーを押します。

 

 

◎「HTML表示」を再度開くと、簡略処理をしたSVGのコードをコピーできます。 区切り線の上側は元のSVG画像のコードで、下側が処理後のSVG画像のコードです。

 

 

上の例では、文字数を半分位に簡略化できています。 変換元の画像サイズを小さくすればもっと文字数を減らせますが、次第に怪しい絵になります。 そういうテストは、「Simple SVG」を使って簡単に試せます。

 

◎「HTML表示」で処理元のSVG画像のコードを手作業で修正したり、処理元のSVG画像を差替えて、新しいSVG画像を処理することもできます。

 

◎「F1」「F2」を押した瞬間に処理が行われます。 処理前と処理後の「SVG画像」が表示されるので、画像の劣化や崩れを確認できます。 元画像は上側に表示され続けますが、「F3」で下側に元画像と同等の画像が表示され、微妙な変化が判ります。

 

◎「path」コードで、「nnn.nnn.nnn」の様な「数値塊」にドットが2個以上ある部分は、処理せずそのままの「数値塊」になります。

 

◎ 簡略の結果が良いもの、余り簡略化にならないもの、簡略化すると画像が劣化するものなど、処理対象により様々です。 小数桁を増すと改善するかもしれません。

 

 

 

「Simple SVG」を利用するには 

このツールは Chrome / Edge / Firefox の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を簡単に説明します。

 

❶「Tampermonkey」を導入します

使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。 以下のページに簡単な導入の説明があるので参照ください。

 

 

❷「Tampermonkey」にスクリプトを登録します

●「Tampermonkey」の「」マークの「新規スクリプト」タブを開きます。

 

 

 

●「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。

 

〔コピー方法〕 軽量シンプルなツール「PreBox Button   」を使うと

  コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」

  の操作で、掲載コードのコピーが可能になります。

 

● 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。

 

 

〔 Simple SVG 〕 ver. 0.2

 

// ==UserScript==
// @name          Simple SVG ⭐
// @namespace  http://tampermonkey.net/
// @version        0.2
// @description  編集画面でpathコードを簡略化 実行キー「F1」「F2」「F3」
// @author         Ameba Blog User
// @match         https://blog.ameba.jp/ucs/entry/srventry*
// @exclude       https://blog.ameba.jp/ucs/entry/srventrylist.do*
// @grant          none
// ==/UserScript==


let retry=0;
let interval=setInterval(wait_target, 100);
function wait_target(){
    retry++;
    if(retry>10){ // リトライ制限 10回 1sec
        clearInterval(interval); }
    let target=document.getElementById('cke_1_contents'); // 監視 target
    if(target){
        clearInterval(interval);
        main(); }}


function main(){
    let editor_iframe;
    let iframe_doc;
    let iframe_body;
    let f_mode; // 処理形式

    let target=document.querySelector('#cke_1_contents');
    let monitor=new MutationObserver( catch_key );
    monitor.observe(target, {childList: true});

    catch_key();

    function catch_key(){
        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){

                iframe_doc.addEventListener('keydown', check_key);
                document.addEventListener('keydown', check_key);

                function check_key(event){
                    if(event.keyCode==112){ // ショートカット「F1」
                        event.preventDefault();
                        event.stopImmediatePropagation();
                        f_mode=0;
                        clear_space();
                        svg_arrange(); }

                    if(event.keyCode==113){ // ショートカット「F2」
                        event.preventDefault();
                        event.stopImmediatePropagation();
                        f_mode=1;
                        clear_space();
                        svg_arrange(); }

                    if(event.keyCode==114){ // ショートカット「F3」
                        event.preventDefault();
                        event.stopImmediatePropagation();
                        f_mode=2;
                        clear_space();
                        svg_arrange(); }

                }}}

    } // catch_key()



    function svg_arrange(){
        if(document.querySelector('.cke_wysiwyg_frame') !=null){ //「通常表示」で実行
            editor_iframe=document.querySelector('.cke_wysiwyg_frame');
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){
                iframe_body=iframe_doc.querySelector('.cke_editable');
                if(iframe_body){

                    let svg_or;
                    let path_or;
                    let svg_n

                    svg_or=iframe_doc.querySelector('.cke_editable svg');// 処理元のSVG
                    if(svg_or){ // 元のSVG画像が無いと動作しない

                        let line=iframe_doc.createElement('p');
                        line.id='line';
                        line.textContent=
                            '=========================================';
                        if(!iframe_body.querySelector('#line')){
                            iframe_body.appendChild(line); }

                        svg_n=svg_or.cloneNode(true);

                        let svg_all=iframe_body.querySelectorAll('svg')
                        for(let k=0; k<svg_all.length-1; k++){
                            svg_all[k+1].remove(); } // 最初のSVG以降は削除
                        iframe_body.appendChild(svg_n);

                        path_or=svg_n.querySelectorAll('path'); // 処理元のSVGの全てのpath
                        for(let k=0; k<path_or.length; k++){
                            let d_or=path_or[k].getAttribute("d");
                            path_or[k].setAttribute("d", path_trim(d_or)); }


                        function path_trim(D_or){
                            let d_all=[];
                            if(f_mode==0){
                                d_all=D_or.split(' '); }
                            else if(f_mode==1 || f_mode==2){
                                d_all=D_or.split(','); }

                            if(f_mode==2){
                                return d_all.join(' '); } // 処理後のpath出力


                            if(f_mode==0 || f_mode==1){
                                let temp_d;
                                temp_d=d_all.join(' ');
                                return arrange_i_code(temp_d); }


                            function arrange_i_code(Temp_d){ // path全体の処理
                                let c_count=0; // 数として処理した文字数
                                let i_code=[]; // 全pathを1文字ずつにした配列
                                let i_code_new=[]; // 処理後の数と文字を格納する配列

                                i_code=Temp_d.split(''); // pathの全体を文字1個ずつに分解
                                for(let k=0; k<i_code.length; k++){
                                    if(i_code[k].match(/[A-Za-z]| |-/g)){
                                        if(c_count==0){
                                            i_code_new.push(i_code[k]); }
                                        else{
                                            i_code_new.push(arr_i_code(c_count, k));
                                            c_count=0; }}
                                    else{ // 数字または「.」の場合
                                        c_count+=1; }}

                                return i_code_new.join(''); // 処理後のpath


                                function arr_i_code(Count, K){
                                    let tmp=[];
                                    let tmp_num; // 再構成した数値(小数を含む)
                                    let tmp_round; // 四捨五入した数値
                                    for(let i=0; i<Count; i++){
                                        tmp.push(i_code[K-Count+i]); }

                                    if(count_dot(tmp.join(''))){ //「.」が1個以内
                                        tmp_num=parseFloat(tmp.join(''));
                                        tmp_round=Math.round(tmp_num);
                                        return tmp_round.toString()+i_code[K]; }
                                    else{ //「.」が2個以上
                                        return tmp.join('')+i_code[K]; }

                                    function count_dot(str){
                                        let count=0;
                                        for (let i=0; i<str.length; i++){
                                            if(str[i]=="."){
                                                count++; }}
                                        if(count<2){
                                            return true; }
                                        else{
                                            return false; }}} // arr_i_code

                            } // arrange_i_code()
                        } // path_trim

                    }}}}} // svg_arrange()



    function clear_space(){ // svgタグの後に増殖する空白行を削除
        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){
                let iframe_body=iframe_doc.querySelector('.cke_editable');
                if(iframe_body){
                    let elements=iframe_body.querySelectorAll('p');
                    for(let k=0; k<elements.length; k++){
                        if(elements[k].childNodes.length=="1"){
                            if(elements[k].firstElementChild){
                                if(elements[k].firstElementChild.tagName=="BR"){
                                    elements[k].remove(); }}}}
                }}}} // clear_space()

} // main()

// ブログ記事にSVGをHTMLで書き込む
// 通常表示に戻り「F1」または「F2」を押すと、処理したSVGが追加される

 

 

 

「Simple SVG」最新版について 

旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。

 

●「Simple SVG」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。