标签集

KYASHA

网络日志搬迁中

文章集

工具集

对抗暗水印



      之前做了个工具为作64*64的像素画。是将一张64*64的位图文件转矢量图,先把图片的每一个像素读出来,排列好,再把排列好的十六进制码写进svg,rect标签,用脚本。

      最后得到图像,除了64行标签什么也没有。好方便Base64编码内联到css中去。svg的图片代码干净,可以读懂,可以和位图一样堆像素,就是这样做比较大。若不是64*64,4k图片转为<rect x="0" y="0" width="1" height="1" style="fill:#040033;" />,这样子在txt中打开,到1,829,330,944 字节了。

      把原来的做头像的工具改一下,一个是不要长宽的限制,第二个是原来的代码有一个多余的操作是hex color = f"#{r:02x}{g:02x}{b:02x}"将RGB值格式化为十六进制字符串,连同以下的f.write(hex_color + "\n")替换成输出 RGB 数值的语句f.write(f"{r},{g},{b}\n")。

      然后逐行读生成的txt,每一条的R值、B值、G值上下随机调动1~3位。

      再把动过的txt文件还原成位图文件,png_file = "Ship of Theseus.png"。不要还原成svg,太大了会大不开。其实txt文件已经打不开了,需要用编译器打开,而且复制不了,因为全选也做不到。











网页的开发原因



      网页开发的时间很短,从4月1日开始编辑至4月4日完工。本来是想编辑视觉效果更复杂的网页,借宿在这的朋友问了句“这么多矢量图一定会画到春天过去了,那时候你已经是找实习的时候了吧?”给我破防了,便突击了一个在样式上简单易制作的网页。











JavaScript筛选功能data标签指向文章



      最开始的做法是一个链接一个html文件,在学校里也是这么做的嘛,但携标签的侧边栏内容都要在每个html中使用的话就很混乱了。

      很大的问题就是代码冗杂,把侧边栏的html结构、css、js渲染逻辑、折叠动画、筛选函数全部复制到每一个文章页面,往后只要侧边栏结构增删、修改标签名称、调整折叠动画速度都必须打开每一个独立 html 文件修改,文件数量越多维护成本指数上升,复制粘贴都会烦,就不想遗漏的故事了。最终整个网站结构彻底狂乱无法维护。

      Web 标准中给了data标签,hmtl里每篇文章
  • 加data-tags="",把文章所属标签存在元素上,css默认把文章display:none隐藏,定义.show{display:block}显示。点击标签时,filterPosts遍历所有文章,用getAttribute('data-tags')取出标签字符串转数组,判断是否包含点击的标签,包含就加show显示、不包含移除隐藏。

          data是数据属性,把标签直接写在文章元素自身上,不要额外的其他什么,标记上的标签自带数据,JS 可以读取,也不需要依赖外部数据,筛选时不需要遍历复杂数据结构。正好合适我这个不明白数据库的,给了我一个通常侧边栏以外的解决方案。不占用额外网络资源,不跳转到新html,页面都不用重新刷新了。重要的是避免大量重复代码,不然一个一个去专门的标签html和文章html覆盖增删的标签也太灾难了。











  • ttf中文字体文件内联css的办法



          生产中不会把完整的过大的中文字体包内联到 css 里。有fontspider这个方案,不做输入的话,可以把使用了的文字打包成一个小字体包,其他没用的字不要,就是字体子集化,从几十 MB 的大包里,只切出网页里实际用到的几十个汉字,生成一个几十 KB 的全新小字体包。文字包内联到css里,体积还不会太大。不过每次更新网站都要把新字加上去就是了。

          不过我现下的情况在开发像素网页时像素字体是必须的,也不能因为网络问题失效了外部资源引用导致视觉效果差错,视觉上有接受不能,资源所以尽量内联,多做同步阻塞,减少异步加载。用 css 加载字体包,要用到 Web Fonts,还是异步加载。

          图片没办法,只能用三方库,直接监听图片加载,加载结束才浏览网页。

          我想编辑出美观的网页,无论网络环境好坏的人们,能看到同样的画面,点进网页的人将承受首屏加载的代价,对不起喵。

          字体转换网站Font Squirrel可以将tff、otf、wpff和woff2等字体包格式,转换成 @font-face 格式,随意地插入随便哪个标签。不过因为国外网站网速慢,中文字体的转换会很久,我想网站是为了转换英文字体包而设计的,不考虑过大的中文字体包。

          为了方便,就在本地浏览器编码tff好了,写一个html工具在浏览器中打开

    .html
    <input type="file" id="fontFile" onchange="encodeFont()">
    <script>
    function encodeFont() {
      const file = document.getElementById('fontFile').files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = (e) => {
        console.log(e.target.result);
        showToast("已输出Base64,打开控制台");
      };
      reader.readAsDataURL(file);
    }
    </script>
          这段放在hmtl语法框架里打开。

          点击按钮,上传ttf字体文件。开浏览器控制台把复制下来的Base64 编码复制在data:font/truetype;charset=utf-8;base64,的后面,替换...的部分。

    .css
    @font-face {
        font-family: 'cnttf';
        src: url(data:font/truetype;charset=utf-8;base64, ... ) format('truetype');
    }
    			
    任意标签 {
        font-family: 'cnttf', sans-serif;
    }
          字体不生效的可能原因,我遇到的是通用二进制mime需要更改为tff字体mime,url后的data:application/octet-stream要改成data:font/truetype;base64,不然浏览器不当成字体解析。字体乱码就是编码声明出问题了,在<head>前加<meta charset="UTF-8">。最后检查下font-family的命名一致。











    生成随机字符



    .html
    <!DOCTYPE html>
    <html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
        <title>随机字符生成</title>
    </head>
    <body>
    <div style="display: flex; justify-content: center; align-items: center; flex-direction: column;">
        <div class="container">
            <h1>随机字符生成</h1>
            <div>
                <label for="passwordOutput" style="font-family: monospace; font-weight: bold;">生成字符:</label><br>
                <input type="text" id="passwordOutput" class="password-field" readonly="" size="40" value="点击生成" style="background-color: rgb(248, 248, 248); border: 1px solid rgb(170, 170, 170); padding: 6px;">
            </div>
            
            <div style="margin-top: 15px;">
                <button id="generateBtn" style="font-family: monospace; padding: 5px 12px; cursor: pointer;">生成新字符</button>
                <button id="copyBtn" style="font-family: monospace; padding: 5px 12px; cursor: pointer; margin-left: 12px;">复制到剪贴板</button>
            </div>
            <hr>
            <div style="margin-bottom: 15px;">
                <label for="lengthSlider" style="font-family: monospace; font-weight: bold;">字符长度:</label>
                <input type="range" id="lengthSlider" min="3" max="32" value="12" step="1" style="width: 220px; vertical-align: middle;">
                <input type="number" id="lengthNumber" min="3" max="32" value="12" step="1" style="width: 55px; font-family: monospace; text-align: center; margin-left: 8px;">
                <span style="font-family: monospace; margin-left: 8px;">字符</span>
            </div>
            <fieldset style="border: 1px solid #aaa; padding: 10px 15px; display: inline-block; margin-top: 8px;">
                <legend style="font-family: monospace; font-weight: bold;">字符池</legend>
                <div style="display: flex; flex-wrap: wrap; gap: 18px; justify-content: center;">
                    <label style="font-family: monospace;">
                        <input type="checkbox" id="useUpper" checked=""> 大写字母 (A-Z)
                    </label>
                    <label style="font-family: monospace;">
                        <input type="checkbox" id="useLower" checked=""> 小写字母 (a-z)
                    </label>
                    <label style="font-family: monospace;">
                        <input type="checkbox" id="useDigits" checked=""> 数字 (0-9)
                    </label>
                    <label style="font-family: monospace;">
                        <input type="checkbox" id="useSpecial"> 特殊符号 (!@#$%^&*...)
                    </label>
                </div>
            </fieldset>
            <div id="statusMsg" style="font-family: monospace; margin-top: 15px; font-size: 0.85rem; color: rgb(44, 62, 44); min-height: 2.2em;"></div>
        </div>
    </div>
    
    <script>
        (function() {
            const passwordOutput = document.getElementById('passwordOutput');
            const generateBtn = document.getElementById('generateBtn');
            const copyBtn = document.getElementById('copyBtn');
            const lengthSlider = document.getElementById('lengthSlider');
            const lengthNumber = document.getElementById('lengthNumber');
            const useUpperChk = document.getElementById('useUpper');
            const useLowerChk = document.getElementById('useLower');
            const useDigitsChk = document.getElementById('useDigits');
            const useSpecialChk = document.getElementById('useSpecial');
            const statusSpan = document.getElementById('statusMsg');
            
            const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            const LOWER = 'abcdefghijklmnopqrstuvwxyz';
            const DIGITS = '0123456789';
            const SPECIAL = '!@#$%^&*()_+-=[]{}|;:,.<>?';
            
            function showMessage(msg, isError = false) {
                statusSpan.textContent = msg;
                statusSpan.style.color = isError ? '#aa2e2e' : '#2c6e2c';
                setTimeout(() => {
                    if (statusSpan.textContent === msg) {
                        statusSpan.textContent = '';
                        statusSpan.style.color = '#2c3e2c';
                    }
                }, 2800);
            }
            
            function secureRandomInt(min, max) {
                const range = max - min + 1;
                if (range <= 0) return min;
                const maxValid = Math.floor(0xFFFFFFFF / range) * range;
                let randomVal;
                const randomArray = new Uint32Array(1);
                do {
                    crypto.getRandomValues(randomArray);
                    randomVal = randomArray[0];
                } while (randomVal >= maxValid);
                return min + (randomVal % range);
            }
            
            function randomCharFromSet(charSet) {
                if (!charSet || charSet.length === 0) return '';
                const idx = secureRandomInt(0, charSet.length - 1);
                return charSet[idx];
            }
            
            function shuffleArray(arr) {
                for (let i = arr.length - 1; i > 0; i--) {
                    const j = secureRandomInt(0, i);
                    [arr[i], arr[j]] = [arr[j], arr[i]];
                }
                return arr;
            }
            
            function generatePassword(length, useUpper, useLower, useDigits, useSpecial) {
                const activeSets = [];
                if (useUpper) activeSets.push(UPPER);
                if (useLower) activeSets.push(LOWER);
                if (useDigits) activeSets.push(DIGITS);
                if (useSpecial) activeSets.push(SPECIAL);
                
                if (activeSets.length === 0) {
                    activeSets.push(LOWER);
                    useLowerChk.checked = true;
                    showMessage("未选择任何字符集,已自动启用小写字母", false);
                }
                
                const minRequired = activeSets.length;
                let finalLength = length;
                if (finalLength < minRequired) {
                    finalLength = minRequired;
                    lengthSlider.value = finalLength;
                    lengthNumber.value = finalLength;
                    showMessage(`长度不足以保证每个类别至少一个字符,已自动调整为 ${finalLength}`, false);
                }
                
                let fullCharPool = '';
                for (let set of activeSets) {
                    fullCharPool += set;
                }
                if (fullCharPool.length === 0) {
                    return "error";
                }
                
                const passwordChars = [];
                
                for (let set of activeSets) {
                    const guaranteedChar = randomCharFromSet(set);
                    passwordChars.push(guaranteedChar);
                }
                
                const remaining = finalLength - activeSets.length;
                if (remaining > 0) {
                    for (let i = 0; i < remaining; i++) {
                        const randChar = randomCharFromSet(fullCharPool);
                        passwordChars.push(randChar);
                    }
                }
                
                const shuffled = shuffleArray([...passwordChars]);
                return shuffled.join('');
            }
            
            function updateLengthConstraints() {
                let activeCount = 0;
                if (useUpperChk.checked) activeCount++;
                if (useLowerChk.checked) activeCount++;
                if (useDigitsChk.checked) activeCount++;
                if (useSpecialChk.checked) activeCount++;
                if (activeCount === 0) activeCount = 1;
                
                const minLen = activeCount;
                const currentLen = parseInt(lengthSlider.value, 10);
                
                lengthSlider.min = minLen;
                lengthNumber.min = minLen;
                
                if (currentLen < minLen) {
                    lengthSlider.value = minLen;
                    lengthNumber.value = minLen;
                    showMessage(`字符集最少需要 ${minLen} 位长度,已自动调整`, false);
                }
            }
            
            function enforceAtLeastOneCheckbox() {
                const anyChecked = useUpperChk.checked || useLowerChk.checked || useDigitsChk.checked || useSpecialChk.checked;
                if (!anyChecked) {
                    useLowerChk.checked = true;
                    showMessage("至少需要一种字符类型,已重新启用小写字母", false);
                    updateLengthConstraints();
                }
            }
            
            function syncLengthInputs() {
                const sliderVal = parseInt(lengthSlider.value, 10);
                const numVal = parseInt(lengthNumber.value, 10);
                if (sliderVal !== numVal) {
                    lengthNumber.value = sliderVal;
                }
                let current = parseInt(lengthNumber.value, 10);
                const minVal = parseInt(lengthSlider.min, 10);
                const maxVal = parseInt(lengthSlider.max, 10);
                if (isNaN(current)) current = minVal;
                if (current < minVal) current = minVal;
                if (current > maxVal) current = maxVal;
                if (current !== parseInt(lengthNumber.value, 10)) {
                    lengthNumber.value = current;
                    lengthSlider.value = current;
                }
            }
            
            function refreshPassword() {
                enforceAtLeastOneCheckbox();
                updateLengthConstraints();
                syncLengthInputs();
                
                let lengthVal = parseInt(lengthSlider.value, 10);
                const minPossible = parseInt(lengthSlider.min, 10);
                if (lengthVal < minPossible) lengthVal = minPossible;
                const useUpper = useUpperChk.checked;
                const useLower = useLowerChk.checked;
                const useDigits = useDigitsChk.checked;
                const useSpecial = useSpecialChk.checked;
                
                if (!useUpper && !useLower && !useDigits && !useSpecial) {
                    useLowerChk.checked = true;
                    showMessage("字符集缺失,启用小写字母作为后备", false);
                    return refreshPassword();
                }
                
                try {
                    const newPassword = generatePassword(lengthVal, useUpper, useLower, useDigits, useSpecial);
                    if (newPassword && newPassword.length === lengthVal) {
                        passwordOutput.value = newPassword;
                        showMessage("新字符已生成 | 长度: " + newPassword.length, false);
                    } else {
                        console.warn("生成字符长度不一致,重新尝试");
                        const fallbackPwd = generatePassword(lengthVal, useUpper, useLower, useDigits, useSpecial);
                        passwordOutput.value = fallbackPwd;
                        showMessage("字符已生成 (安全随机)", false);
                    }
                } catch (err) {
                    console.error(err);
                    passwordOutput.value = "生成失败,请重试";
                    showMessage("生成错误,请检查配置", true);
                }
            }
            
            async function copyPasswordToClipboard() {
                const pwd = passwordOutput.value;
                if (!pwd || pwd === "点击生成" || pwd === "生成失败,请重试") {
                    showMessage("没有有效字符可复制,请先生成", true);
                    return;
                }
                try {
                    await navigator.clipboard.writeText(pwd);
                    showMessage("字符已复制到剪贴板", false);
                    passwordOutput.style.backgroundColor = "#e6f0e6";
                    setTimeout(() => {
                        passwordOutput.style.backgroundColor = "#f8f8f8";
                    }, 300);
                } catch (err) {
                    passwordOutput.select();
                    const success = document.execCommand('copy');
                    if (success) {
                        showMessage("字符已复制 (传统方式)", false);
                    } else {
                        showMessage("复制失败了,手动选取", true);
                    }
                    passwordOutput.setSelectionRange(0, 0);
                }
            }
            
            generateBtn.addEventListener('click', () => {
                refreshPassword();
            });
            
            copyBtn.addEventListener('click', () => {
                copyPasswordToClipboard();
            });
            
            lengthSlider.addEventListener('input', function() {
                let val = parseInt(this.value, 10);
                const minLimit = parseInt(this.min, 10);
                if (val < minLimit) val = minLimit;
                if (val > 32) val = 32;
                lengthSlider.value = val;
                lengthNumber.value = val;
                let activeCount = 0;
                if (useUpperChk.checked) activeCount++;
                if (useLowerChk.checked) activeCount++;
                if (useDigitsChk.checked) activeCount++;
                if (useSpecialChk.checked) activeCount++;
                if (activeCount === 0) activeCount = 1;
                if (val < activeCount) {
                    showMessage(`注意:当前长度小于所选字符类别数(${activeCount}),点击生成将自动调整`, false);
                } else {
                    if (statusSpan.textContent.includes("小于所选字符类别数")) statusSpan.textContent = "";
                }
            });
            
            lengthNumber.addEventListener('input', function() {
                let val = parseInt(this.value, 10);
                if (isNaN(val)) val = lengthSlider.min;
                const minLimit = parseInt(lengthSlider.min, 10);
                const maxLimit = 32;
                if (val < minLimit) val = minLimit;
                if (val > maxLimit) val = maxLimit;
                lengthNumber.value = val;
                lengthSlider.value = val;
                let activeCount = 0;
                if (useUpperChk.checked) activeCount++;
                if (useLowerChk.checked) activeCount++;
                if (useDigitsChk.checked) activeCount++;
                if (useSpecialChk.checked) activeCount++;
                if (activeCount === 0) activeCount = 1;
                if (val < activeCount) {
                    showMessage(`长度小于字符类别数(${activeCount}),生成时自动修正`, false);
                }
            });
            
            function onCheckboxChange() {
                enforceAtLeastOneCheckbox();
                updateLengthConstraints();
                syncLengthInputs();
                showMessage("字符集已更新,点击「生成新字符」生效", false);
            }
            
            useUpperChk.addEventListener('change', onCheckboxChange);
            useLowerChk.addEventListener('change', onCheckboxChange);
            useDigitsChk.addEventListener('change', onCheckboxChange);
            useSpecialChk.addEventListener('change', onCheckboxChange);
            
            function init() {
                if (!useUpperChk.checked) useUpperChk.checked = true;
                if (!useLowerChk.checked) useLowerChk.checked = true;
                if (!useDigitsChk.checked) useDigitsChk.checked = true;
                updateLengthConstraints();
                syncLengthInputs();
                refreshPassword();
            }
            
            init();
            
            window.addEventListener('load', function() {
                if (!passwordOutput.value || passwordOutput.value === "点击生成") {
                    refreshPassword();
                }
            });
            
            setInterval(function() {
                const currentLen = parseInt(lengthSlider.value, 10);
                let activeCount = 0;
                if (useUpperChk.checked) activeCount++;
                if (useLowerChk.checked) activeCount++;
                if (useDigitsChk.checked) activeCount++;
                if (useSpecialChk.checked) activeCount++;
                if (activeCount === 0) activeCount = 1;
                if (currentLen < activeCount) {
                    updateLengthConstraints();
                    syncLengthInputs();
                }
            }, 10000);
        })();
    </script>
    
    </body></html>
    ttf转Base64url编码



    .html
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>字体转Base64工具</title>
    </head>
    <body>
        <h2>ttf转Base64url编码</h2>
        <p>选择.ttf、.otf字体文件,转换后的完整DataURL会表示在下方文本区,点击按钮复制</p>
        <input type="file" id="fontFile" accept=".ttf,.otf">
        <div id="fileInfo"></div>
        <table border="1" cellpadding="8" cellspacing="0">
            <tr>
                <td valign="top">
                    <button id="copyBtn">复制Base64结果</button>
                    <div>点击按钮复制完整字符串</div>
                 </td>
                <td width="95%">
                    <textarea id="resultArea" readonly rows="12" cols="100" wrap="soft" placeholder="请选择字体文件(.ttf / .otf),转换后会表示Base64编码结果...结果将完整展示在此区域,点击按钮即可复制"></textarea>
                    <div id="resultMeta"></div>
                 </td>
            </tr>
        </table>
        <div id="statusMessage"></div>
    
        <script>
            (function() {
                const fileInput = document.getElementById('fontFile');
                const resultArea = document.getElementById('resultArea');
                const copyBtn = document.getElementById('copyBtn');
                const fileInfoDiv = document.getElementById('fileInfo');
                const resultMetaDiv = document.getElementById('resultMeta');
                const statusMsgDiv = document.getElementById('statusMessage');
    
                let pendingFile = null;
    
                function updateFileInfo(file) {
                    if (!file) {
                        fileInfoDiv.innerHTML = '';
                        return;
                    }
                    const sizeKB = (file.size / 1024).toFixed(2);
                    const sizeMB = (file.size / (1024 * 1024)).toFixed(2);
                    let sizeStr = sizeKB + ' KB';
                    if (file.size > 1024 * 1024) {
                        sizeStr = sizeMB + ' MB (' + sizeKB + ' KB)';
                    }
                    fileInfoDiv.innerHTML = '已选择: ' + file.name + '  |  大小: ' + sizeStr;
                }
    
                function clearResult(placeholderMsg) {
                    if (placeholderMsg) {
                        resultArea.value = placeholderMsg;
                    } else {
                        resultArea.value = '';
                    }
                    resultMetaDiv.innerHTML = '';
                }
    
                function displayResult(dataURL, file) {
                    if (!dataURL) {
                        resultArea.value = '转换失败:未获取到有效数据';
                        resultMetaDiv.innerHTML = '';
                        return;
                    }
                    resultArea.value = dataURL;
                    const totalLength = dataURL.length;
                    const base64Part = dataURL.split(',')[1] || '';
                    const rawBase64Len = base64Part.length;
                    resultMetaDiv.innerHTML = '转换终了 | 完整字符串长度: ' + totalLength + ' 字符 | Base64数据长度: ' + rawBase64Len + ' 字符 | 文件: ' + file.name;
                    statusMsgDiv.innerHTML = '转换终了结果已表示在下方文本区,点击左侧“复制Base64结果”按钮即可复制完整字符串。';
                    console.log('[字体转Base64] 转换终了,DataURL长度:', totalLength);
                }
    
                function showError(errorMsg, file) {
                    resultArea.value = '转换出错: ' + errorMsg + '\n请检查字体文件是否有效或重试。';
                    resultMetaDiv.innerHTML = '错误: ' + errorMsg;
                    statusMsgDiv.innerHTML = '处理失败: ' + errorMsg;
                    console.error('[字体转Base64] 错误:', errorMsg, file ? file.name : '');
                }
    
                function convertFontToBase64(file) {
                    if (!file) {
                        showError('未选择有效文件', null);
                        return;
                    }
    
                    updateFileInfo(file);
                    
                    const fileName = file.name.toLowerCase();
                    if (!fileName.endsWith('.ttf') && !fileName.endsWith('.otf')) {
                        statusMsgDiv.innerHTML = '注意: 文件 "' + file.name + '" 扩展名不是 .ttf 或 .otf,转换仍继续,但确保是有效字体文件。';
                    } else {
                        statusMsgDiv.innerHTML = '正在转换字体文件 "' + file.name + '",请稍候...';
                    }
                    
                    resultArea.value = '正在读取并编码字体文件,请稍候...\n(大文件可能需要几秒钟)';
                    resultMetaDiv.innerHTML = '';
                    
                    const currentFileToken = file;
                    pendingFile = currentFileToken;
                    
                    const reader = new FileReader();
                    
                    reader.onload = function(ev) {
                        if (pendingFile !== currentFileToken) {
                            console.log('[忽略] 文件 "' + currentFileToken.name + '" 转换终了,但已被新文件覆盖,不更新界面。');
                            return;
                        }
                        const dataURL = ev.target.result;
                        if (dataURL && typeof dataURL === 'string' && dataURL.startsWith('data:')) {
                            displayResult(dataURL, currentFileToken);
                        } else {
                            showError('读取结果无效,未能生成有效的DataURL', currentFileToken);
                        }
                        pendingFile = null;
                    };
                    
                    reader.onerror = function(err) {
                        if (pendingFile !== currentFileToken) {
                            console.log('[忽略错误] 文件 "' + currentFileToken.name + '" 读取失败但已被覆盖。');
                            return;
                        }
                        showError('文件读取失败: ' + (reader.error ? reader.error.message : '未知错误'), currentFileToken);
                        pendingFile = null;
                    };
                    
                    reader.onabort = function() {
                        if (pendingFile === currentFileToken) {
                            showError('读取操作已取消', currentFileToken);
                            pendingFile = null;
                        }
                    };
                    
                    reader.readAsDataURL(file);
                }
    
                fileInput.addEventListener('change', function(e) {
                    const file = e.target.files[0];
                    if (!file) {
                        fileInfoDiv.innerHTML = '';
                        resultArea.value = '未选择任何字体文件,请选择 .ttf 或 .otf 文件。';
                        resultMetaDiv.innerHTML = '';
                        statusMsgDiv.innerHTML = '';
                        pendingFile = null;
                        return;
                    }
                    convertFontToBase64(file);
                });
    
                copyBtn.addEventListener('click', async function() {
                    const contentToCopy = resultArea.value;
                    if (!contentToCopy || 
                        contentToCopy === '未选择任何字体文件,请选择 .ttf 或 .otf 文件。' ||
                        contentToCopy.startsWith('正在读取并编码字体文件') ||
                        contentToCopy.startsWith('转换出错') ||
                        contentToCopy.startsWith('转换失败') ||
                        contentToCopy.includes('请选择字体文件')) {
                        alert('没有可复制的内容,请先选择一个字体文件并等待转换完成。');
                        return;
                    }
                    
                    if (!contentToCopy.includes('data:') || !contentToCopy.includes('base64')) {
                        alert('当前文本不是有效的Base64字体数据,请重新转换字体文件。');
                        return;
                    }
                    
                    try {
                        await navigator.clipboard.writeText(contentToCopy);
                        alert('复制成功\nBase64字符串已存入剪贴板,可直接在CSS或代码中使用。\n(字符串总长度: ' + contentToCopy.length + ' 字符)');
                        statusMsgDiv.innerHTML = '复制成功,已复制 ' + contentToCopy.length + ' 个字符到剪贴板。';
                        setTimeout(function() {
                            if (statusMsgDiv.innerHTML.indexOf('复制成功') !== -1) {
                                if (resultArea.value && resultArea.value.startsWith('data:')) {
                                    statusMsgDiv.innerHTML = '转换结果已就绪,可继续复制或选择新字体。';
                                } else {
                                    statusMsgDiv.innerHTML = '';
                                }
                            }
                        }, 2500);
                    } catch (err) {
                        console.error('复制失败:', err);
                        try {
                            resultArea.select();
                            resultArea.setSelectionRange(0, resultArea.value.length);
                            var success = document.execCommand('copy');
                            if (success) {
                                alert('复制成功(兼容模式)Base64字符串已存入剪贴板。');
                                statusMsgDiv.innerHTML = '复制成功已复制 ' + contentToCopy.length + ' 个字符。';
                            } else {
                                alert('复制失败,请手动选中文本域内容后按Ctrl+C复制。');
                            }
                        } catch (fallbackErr) {
                            alert('复制失败,请手动选中文本域中的内容后复制。\n错误详情: ' + fallbackErr.message);
                        }
                    }
                });
    
                resultArea.value = '等待选择字体文件...\n点击上方“选择文件”按钮,选取 .ttf 或 .otf 字体,转换后将在此处表示完整的Base64 DataURL。\n左侧【复制Base64结果】按钮可一键复制。';
                resultMetaDiv.innerHTML = '';
                fileInfoDiv.innerHTML = '';
                statusMsgDiv.innerHTML = '选择字体文件后自动转换,结果会表示在下方文本区。';
            })();
        </script>
    </body>
    </html>

    華奢

    网页使用HBuilderX编辑
    很优秀的IDE
    只有一个 .html文件

    粤ICP备2026040290号