<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel rdf:about="https://let.hatelabo.jp/Haru0320/rss">
    <link>https://let.hatelabo.jp/Haru0320/rss</link>
    <description></description>
    <title>Bookmarklets from Haru0320</title>
    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="https://let.hatelabo.jp/Haru0320/let/lbDel6iUgMAA"/>
      </rdf:Seq>
    </items>
  </channel>
  <item rdf:about="https://let.hatelabo.jp/Haru0320/let/lbDel6iUgMAA">
    <link>https://let.hatelabo.jp/Haru0320/let/lbDel6iUgMAA</link>
    <dc:date>2026-08-22T08:54:20Z</dc:date>
    <description>Dropboxから「260815_超かぐや姫おつかれさま本」の画像をPDF形式でダウンロードする</description>
    <dc:creator>Haru0320</dc:creator>
    <title>[Let] PDF化！「260815_超かぐや姫おつかれさま本」</title>
    <content:encoded>&lt;a href="javascript:%28async%28%29%3D%3E%7Bconst%20wait%3Dmsec%3D%3Enew%20Promise%28%28resolve%3D%3EsetTimeout%28resolve%2Cmsec%29%29%29%3Bconst%20script%3Ddocument.createElement%28%27script%27%29%3Bscript.nonce%3Ddocument.querySelector%28%27script%5Bnonce%5D%27%29.nonce%3Bscript.src%3D%27https%3A%2F%2Fcdnjs.cloudflare.com%2Fajax%2Flibs%2Fjspdf%2F4.2.1%2Fjspdf.umd.min.js%27%3Bdocument.documentElement.append%28script%29%3Bfor%28let%20ci%3D0%3Bci%3C50%3Bci%2B%2B%29%7Bif%28typeof%20jspdf%21%3D%3D%27undefined%27%29break%3Bawait%20wait%28100%29%7Dif%28typeof%20jspdf%3D%3D%3D%27undefined%27%29%7Balert%28%27Failed%20to%20load%20jsPDF%27%29%3Breturn%7Dconst%20jsPDF%3Djspdf.jsPDF%3Bconst%20document_title%3Ddocument.querySelector%28%27title%27%29.textContent%3Bconst%20max_page%3D%5B...document.querySelector%28%27%5Bdata-testid%3D%22page-count%22%5D%2C%5Brole%3D%22document%22%5D%3Ediv%3E.dig-Layer%3Ediv%3E.dig-Text%27%29.childNodes%5D.filter%28%28elem%3D%3Eelem.nodeName%3D%3D%27%23text%27%29%29.map%28%28elm%3D%3Eelm.nodeValue%29%29.join%28%27%27%29.match%28%2F%5C%2F%5Cs%2A%28%5Cd%2B%29%2F%29%5B1%5D-0%3Bconst%20max_page_strlen%3D%60%24%7Bmax_page%2A2-2%7D%60.length%3Bconst%20sample_img%3Ddocument.querySelector%28%27%5Bdata-testid%3D%22preview-viewport%22%5D%20%5Bid%5E%3D%22pageContainer%22%5D%5Bdata-index%5D%20img%27%29%3Bconst%20img_url%3Dnew%20URL%28sample_img.src%29%3Bconst%20img_ext%3Dimg_url.pathname.match%28%2F%5C.%5B%5E.%5D%2B%24%2F%29%5B0%5D%3Bconst%20progress_container%3Ddocument.createElement%28%27div%27%29%3BObject.assign%28progress_container.style%2C%7Bposition%3A%27fixed%27%2Ctop%3A%274px%27%2Cleft%3A%278px%27%2CzIndex%3A1e4%2Cpadding%3A%274px%27%2CfontFamily%3A%27monospace%27%2Ccolor%3A%27black%27%2CbackgroundColor%3A%27lightyellow%27%7D%29%3Bdocument.documentElement.append%28progress_container%29%3Bconst%20show_progress%3Dpage_number%3D%3Eprogress_container.textContent%3D%60ZIP%28PDF%29%3A%20%24%7Bpage_number%7D%20%2F%20%24%7Bmax_page%7D%60%3Bconst%20canvas%3Ddocument.createElement%28%27canvas%27%29%3BObject.assign%28canvas.style%2C%7Bdisplay%3A%27none%27%7D%29%3Bdocument.documentElement.append%28canvas%29%3Bconst%20context%3Dcanvas.getContext%28%272d%27%29%3Bconst%20canvas_to_blob%3D%28%29%3D%3Enew%20Promise%28%28resolve%3D%3Ecanvas.toBlob%28%28blob%3D%3Eresolve%28blob%29%29%2C%27image%2Fpng%27%2C1%29%29%29%3Bconst%20get_img_filename%3Dimg_number%3D%3E%60%24%7B%28%27%27%2Bimg_number%29.padStart%28max_page_strlen%2C0%29%7D.png%60%3Bconst%20pdf%3Dnew%20jsPDF%28%7Borientation%3A%27portrait%27%2Cunit%3A%27mm%27%2Cformat%3A%27a4%27%7D%29%3Bpdf.setProperties%28%7Btitle%3A%27%E8%B6%85%E3%81%8B%E3%81%90%E3%82%84%E5%A7%AB%EF%BC%81%E3%81%8A%E3%81%A4%E3%81%8B%E3%82%8C%E3%81%95%E3%81%BE%E6%9C%AC%27%2Cauthor%3A%27%E3%83%91%E3%83%B3%E3%82%B1%E3%83%BC%E3%82%AD%E3%82%AA%E3%82%AF%E3%83%88%E3%83%91%E3%82%B9%27%2Ckeywords%3A%27C108%2C20260815%27%2Ccreator%3A%27Unofficial%20bookmarklet%20%28powered%20by%20jsPDF%29%27%7D%29%3Bconst%20pdf_width%3Dpdf.internal.pageSize.getWidth%28%29%3Bconst%20pdf_height%3Dpdf.internal.pageSize.getHeight%28%29%3Bconst%20pdf_ratio%3Dpdf_height%2Fpdf_width%3Bconst%20add_img_to_pdf%3D%28img_bitmap%2Coffset_x%2Cslice_width%29%3D%3E%7Bcanvas.width%3Dslice_width%3Bcanvas.height%3Dimg_bitmap.height%3Bcontext.drawImage%28img_bitmap%2Coffset_x%2C0%2Cslice_width%2Cimg_bitmap.height%2C0%2C0%2Cslice_width%2Cimg_bitmap.height%29%3Bconst%20img_ratio%3Dimg_bitmap.height%2Fslice_width%3Blet%20pdf_img_width%2Cpdf_img_height%3Bif%28img_ratio%3Cpdf_ratio%29%7Bpdf_img_width%3Dpdf_width%3Bpdf_img_height%3Dpdf_width%2Aimg_ratio%7Delse%7Bpdf_img_height%3Dpdf_height%3Bpdf_img_width%3Dpdf_height%2Fimg_ratio%7Dpdf.addImage%28%7BimageData%3Acanvas.toDataURL%28%27image%2Fpng%27%29%2Cformat%3A%27PNG%27%2Cx%3A%28pdf_width-pdf_img_width%29%2F2%2Cy%3A%28pdf_height-pdf_img_height%29%2F2%2Cwidth%3Apdf_img_width%2Cheight%3Apdf_img_height%7D%29%3Bpdf.addPage%28%29%7D%3Bfor%28let%20page_number%3D0%2Cimg_number%3D1%3Bpage_number%3Cmax_page%3Bpage_number%2B%2B%29%7Bshow_progress%28page_number%2B1%29%3Bimg_url.searchParams.set%28%27page%27%2Cpage_number%29%3Bconst%20response%3Dawait%20fetch%28img_url.href%29%3Bif%28%21response.ok%29%7Bconsole.error%28%60Fetch%20failure%20%24%7Bpage_number%2B1%7D%3A%20%24%7Bimg_url.href%7D%60%29%3Bcontinue%7Dconst%20img_data%3Dawait%20response.blob%28%29%3Bconst%20img_bitmap%3Dawait%20createImageBitmap%28img_data%29%3Bconsole.log%28%60%24%7Bpage_number%2B1%7D%3A%20%24%7Bimg_bitmap.width%7Dx%24%7Bimg_bitmap.height%7D%60%29%3Bif%28img_bitmap.width%3C%3Dimg_bitmap.height%29%7Badd_img_to_pdf%28img_bitmap%2C0%2Cimg_bitmap.width%29%3Bimg_number%2B%2B%7Delse%7Bconst%20slice_width%3DMath.floor%28img_bitmap.width%2F2%29%3Badd_img_to_pdf%28img_bitmap%2C0%2Cslice_width%29%3Bimg_number%2B%2B%3Badd_img_to_pdf%28img_bitmap%2Cslice_width%2Cimg_bitmap.width-slice_width%29%3Bimg_number%2B%2B%7D%7Dpdf.deletePage%28pdf.getNumberOfPages%28%29%29%3Btry%7Bpdf.save%28%60%24%7Bdocument_title.replace%28%2F%5C.%5B%5E.%5D%2B%24%2F%2C%27%27%29%7D.pdf%60%29%7Dcatch%28error%29%7Balert%28error%29%7Dcanvas.remove%28%29%3Bprogress_container.remove%28%29%7D%29%28%29%3B"&gt;PDF化！「260815_超かぐや姫おつかれさま本」&lt;/a&gt;&lt;pre&gt;/*
 * @title PDF化！「260815_超かぐや姫おつかれさま本」
 * @description Dropboxから「260815_超かぐや姫おつかれさま本」の画像をPDF形式でダウンロードする
 * @include https://www.dropbox.com/*
 * @license MIT License
 * @require 
 * @javascript_url
 */

/*
//「260815_超かぐや姫おつかれさま本」公開ページはこちら↓
// https://www.dropbox.com/scl/fi/2dz2rnch41n0ym6svx2v1/260815_.pdf?rlkey=79hr9bq43shkosbwhe93003lu&amp;amp;st=z03mvt95&amp;amp;e=1&amp;amp;dl=0
// ※関連ツイート(ポスト) → https://x.com/momocyo/status/2088566368677331068
*/

(async () =&amp;gt; {
    const wait = msec =&amp;gt; new Promise(resolve =&amp;gt; setTimeout(resolve, msec));
    const script = document.createElement('script');
    script.nonce = document.querySelector('script[nonce]').nonce;
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/4.2.1/jspdf.umd.min.js';
    document.documentElement.append(script);
    for (let ci=0; ci &amp;lt; 50; ci++) {
        if (typeof jspdf !== 'undefined') break;
        await wait(100);
    }
    if (typeof jspdf === 'undefined') {
        alert('Failed to load jsPDF');
        return;
    }
    const jsPDF = jspdf.jsPDF;
    const document_title = document.querySelector('title').textContent;
    const max_page = [...document.querySelector('[data-testid=&amp;quot;page-count&amp;quot;],[role=&amp;quot;document&amp;quot;]&amp;gt;div&amp;gt;.dig-Layer&amp;gt;div&amp;gt;.dig-Text').childNodes].filter(elem=&amp;gt;elem.nodeName=='#text').map(elm=&amp;gt;elm.nodeValue).join('').match(/\/\s*(\d+)/)[1] - 0;
    const max_page_strlen = `${max_page*2-2}`.length;
    const sample_img = document.querySelector('[data-testid=&amp;quot;preview-viewport&amp;quot;] [id^=&amp;quot;pageContainer&amp;quot;][data-index] img');
    const img_url = new URL(sample_img.src);
    const img_ext = img_url.pathname.match(/\.[^.]+$/)[0];
    const progress_container = document.createElement('div');
    Object.assign(progress_container.style, {
        position: 'fixed',
        top: '4px',
        left: '8px',
        zIndex: 10000,
        padding: '4px',
        fontFamily: 'monospace',
        color: 'black',
        backgroundColor: 'lightyellow',
    });
    document.documentElement.append(progress_container);
    const show_progress = page_number =&amp;gt; progress_container.textContent = `ZIP(PDF): ${page_number} / ${max_page}`;
    const canvas = document.createElement('canvas');
    Object.assign(canvas.style, {
        display: 'none',
    });
    document.documentElement.append(canvas);
    const context = canvas.getContext('2d');
    const canvas_to_blob = () =&amp;gt; new Promise(resolve =&amp;gt; canvas.toBlob(blob =&amp;gt; resolve(blob), 'image/png', 1));
    const get_img_filename = img_number =&amp;gt; `${(''+img_number).padStart(max_page_strlen, 0)}.png`;
    const pdf = new jsPDF({
        orientation: 'portrait',
        unit: 'mm',
        format: 'a4',
    });
    pdf.setProperties({
        title: '超かぐや姫！おつかれさま本',
        author: 'パンケーキオクトパス',
        keywords: 'C108,20260815',
        creator: 'Unofficial bookmarklet (powered by jsPDF)'
    });
    const pdf_width = pdf.internal.pageSize.getWidth();
    const pdf_height = pdf.internal.pageSize.getHeight();
    const pdf_ratio = pdf_height / pdf_width;
    const add_img_to_pdf = (img_bitmap, offset_x, slice_width) =&amp;gt; {
        canvas.width = slice_width;
        canvas.height = img_bitmap.height;
        context.drawImage(img_bitmap, offset_x, 0, slice_width, img_bitmap.height, 0, 0, slice_width, img_bitmap.height);
        const img_ratio = img_bitmap.height / slice_width;
        let pdf_img_width, pdf_img_height;
        if (img_ratio &amp;lt; pdf_ratio) {
            pdf_img_width = pdf_width;
            pdf_img_height = pdf_width * img_ratio;
        }
        else {
            pdf_img_height = pdf_height;
            pdf_img_width = pdf_height / img_ratio;
        }
        pdf.addImage({
            imageData: canvas.toDataURL('image/png'),
            format: 'PNG',
            x: (pdf_width - pdf_img_width) / 2,
            y: (pdf_height - pdf_img_height) / 2,
            width: pdf_img_width,
            height: pdf_img_height,
        });
        pdf.addPage();
    };
    for (let page_number=0, img_number=1; page_number &amp;lt; max_page; page_number++) {
        show_progress(page_number+1);
        img_url.searchParams.set('page', page_number);
        const response = await fetch(img_url.href);
        if (! response.ok) {
            console.error(`Fetch failure ${page_number+1}: ${img_url.href}`);
            continue;
        }
        const img_data = await response.blob();
        const img_bitmap = await createImageBitmap(img_data);
        console.log(`${page_number+1}: ${img_bitmap.width}x${img_bitmap.height}`);
        if (img_bitmap.width &amp;lt;= img_bitmap.height) {
            add_img_to_pdf(img_bitmap, 0, img_bitmap.width);
            img_number++;
        }
        else {
            const slice_width = Math.floor(img_bitmap.width / 2);
            add_img_to_pdf(img_bitmap, 0, slice_width);
            img_number++;
            add_img_to_pdf(img_bitmap, slice_width, img_bitmap.width - slice_width);
            img_number++;
        }
    }
    pdf.deletePage(pdf.getNumberOfPages());
    try {
        pdf.save(`${document_title.replace(/\.[^.]+$/,'')}.pdf`);
    }
    catch (error) {
        alert(error);
    }
    canvas.remove();
    progress_container.remove();
})();
&lt;/pre&gt;</content:encoded>
  </item>
</rdf:RDF>
