KDP(電子出版)のメモ 急急如律令

Amazon Kindleダイレクト・パブリッシングでの電子出版や電子書籍の作成販売について、文章やイラストの作成や編集方法について書いています。

ACOファイルを編集するプログラムがなかったのでChatGPTに作らせた

 配色データ(拡張子.aco)があるのだけど、編集できるソフトがadobeのものが多くて、読み込みにしか対応していないことがある。 ChatGPTに「以下のC#コードをJavascriptで書き直して下さい。」の下に下記サイトのサンプルコードを乗せただけ。

qiita.com

そうしたら、acoファイルを読み込んで表示するプログラムが出力された。 「編集結果を保存できるように書き換えて」

ChatGPT「編集結果を保存できるようにするために、ユーザーが編集した色のデータをJSONファイルとしてダウンロードできる機能を追加します。以下はそのための改良版です。」

「jsonではなくACOファイルとして保存するように変更して。」

そしたらACOファイルを編集するプログラムができた。

UIを整えたり項目を増やしたり減らし、HTMLの色コードを読み込ませたりすると完成になりそう。

使用方法 ブラウザでファイルを開く:

index.htmlファイルをブラウザで開きます。 ACOファイルのインポート:

「*.aco」ファイルをインポートするために、ファイル入力ボックスにACOファイルをドラッグ&ドロップするか、クリックしてファイルを選択します。 「Import」ボタンが有効になるので、クリックしてACOファイルを読み込みます。 色の編集:

インポートされた色がリストで表示されます。 各色のカラーピッカーを使用して色を変更できます。色を変更すると、そのRGB値もラベルに表示されます。 ACOファイルの保存:

色の編集が完了したら、「Save」ボタンをクリックしてACOファイルを保存します。 ブラウザがファイルをダウンロードし、新しいACOファイルが保存されます。 注意点 このコードは、ブラウザのFile APIを使用しているため、ローカルファイルシステムへのアクセスを必要とします。一部のブラウザや環境では動作しない場合があります。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Aco Importer</title>
    <style>
        .color-box {
            display: flex;
            align-items: center;
            margin: 10px 0;
        }
        .color-box input[type="color"] {
            margin-right: 10px;
        }
    </style>
</head>
<body>
    <h1>Aco Importer</h1>
    <input type="file" id="fileInput" accept=".aco">
    <button id="importButton" disabled>Import</button>
    <button id="saveButton" disabled>Save</button>
    <div id="colorList"></div>

    <script src="acoImporter.js"></script>
</body>
</html>
document.addEventListener('DOMContentLoaded', () => {
    const fileInput = document.getElementById('fileInput');
    const importButton = document.getElementById('importButton');
    const saveButton = document.getElementById('saveButton');
    const colorList = document.getElementById('colorList');

    let colors = null;

    fileInput.addEventListener('change', () => {
        if (fileInput.files.length > 0) {
            importButton.disabled = false;
        } else {
            importButton.disabled = true;
            saveButton.disabled = true;
        }
    });

    importButton.addEventListener('click', () => {
        const file = fileInput.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = function(event) {
                const arrayBuffer = event.target.result;
                colors = getColors(new Uint8Array(arrayBuffer));
                displayColors(colors);
                saveButton.disabled = false;
            };
            reader.readAsArrayBuffer(file);
        }
    });

    saveButton.addEventListener('click', () => {
        if (colors) {
            const acoData = createAcoFile(colors);
            download('colors.aco', acoData);
        }
    });

    function displayColors(colors) {
        colorList.innerHTML = '';
        colors.forEach(color => {
            const colorBox = document.createElement('div');
            colorBox.classList.add('color-box');

            const colorInput = document.createElement('input');
            colorInput.type = 'color';
            colorInput.value = rgbToHex(color.R, color.G, color.B);
            colorInput.addEventListener('input', (event) => {
                const hex = event.target.value;
                const rgb = hexToRgb(hex);
                color.R = rgb.r;
                color.G = rgb.g;
                color.B = rgb.b;
                color.Color = `rgba(${color.R}, ${color.G}, ${color.B}, 1)`;
            });

            const label = document.createElement('label');
            label.textContent = rgbToHex(color.R, color.G, color.B);

            colorBox.appendChild(colorInput);
            colorBox.appendChild(label);
            colorList.appendChild(colorBox);
        });
    }

    function getColors(array) {
        const colors = [];
        const colorCount = array[3];
        let pos = 6;
        for (let i = 0; i < colorCount; i++) {
            const R = array[pos];
            const G = array[pos + 2];
            const B = array[pos + 4];
            const color = {
                R,
                G,
                B,
                Color: `rgba(${R}, ${G}, ${B}, 1)`
            };
            colors.push(color);
            pos += 10;
        }
        return colors;
    }

    function rgbToHex(r, g, b) {
        return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
    }

    function hexToRgb(hex) {
        const bigint = parseInt(hex.slice(1), 16);
        return {
            r: (bigint >> 16) & 255,
            g: (bigint >> 8) & 255,
            b: bigint & 255
        };
    }

    function createAcoFile(colors) {
        const buffer = new ArrayBuffer(4 + colors.length * 10);
        const view = new DataView(buffer);
        view.setUint16(0, 0); // version
        view.setUint16(2, colors.length); // color count

        let pos = 4;
        colors.forEach(color => {
            view.setUint16(pos, 0); // color space (0 = RGB)
            view.setUint16(pos + 2, color.R * 256); // R
            view.setUint16(pos + 4, color.G * 256); // G
            view.setUint16(pos + 6, color.B * 256); // B
            view.setUint16(pos + 8, 0); // padding
            pos += 10;
        });

        return new Blob([buffer], { type: 'application/octet-stream' });
    }

    function download(filename, data) {
        const element = document.createElement('a');
        element.href = URL.createObjectURL(data);
        element.download = filename;

        document.body.appendChild(element);
        element.click();
        document.body.removeChild(element);
    }
});

問題は出力されたファイルが、acoの仕様に沿っていないことだ。 qiita.com