ホームページ >ウェブフロントエンド >jsチュートリアル >Starlight の PDF への変換: 経験と洞察

Starlight の PDF への変換: 経験と洞察

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2025-01-15 10:24:42714ブラウズ

あなたに、1 週間以内に新しいドキュメント Web サイトを作成するというタスクが与えられたと想像してください。視覚的に魅力的で、高速で、操作が簡単である必要があります。 「やってください」 という指示とともに、大量の *.docs ファイル、画像、スクリーンショットが渡されます。

DocusaurusNextraVitePressDocus など、選択できる優れたツールが多数あります。以前、Starlight を使用してドキュメント Web サイトを構築した素晴らしい経験があったため、このタスクには Starlight を選択しました。しかし、ドキュメントから PDF を生成する機能という欠けている機能を発見しました。そしてそれは要件の1つでした。 「素敵なサイドプロジェクトですね」 私は自分で考えました。

課題に取り組む

最初は簡単そうに思えました。ページを取得し、HTML を解析し、コンテンツをグループ化すれば、出来上がりです!

Starlight を利用した Web サイトには、ドキュメント内を移動するための「次へ」ボタンがあります。 PDF は本質的にページの配列であるため、この「次へ」ボタンを使用してページを 1 つずつ解析するのが合理的であると思われます。 Web サイトは静的ページを生成するため、HTML をフェッチし、必要な部分をクエリして、すべてを組み合わせるスクリプトをすぐに作成しました。ただし、Web サイトのスタイルを保持した PDF を生成することは、より複雑であることが判明しました。ブレーンストーミングを行った結果、Puppeteer が最適なソリューションであることがわかりました。

これでプロセスが明らかになりました:

  1. 開始ページを特定します。 これは、「次へ」ボタンがある最初のページです。
  2. ページ内を移動します。 各ページから見出しとメインコンテンツを抽出し、同時に目次を作成します。
  3. コンテンツを結合します。 改ページと追加のスタイルを追加します。
  4. 最終的な HTML を準備します。 を書き換えます。結果の HTML を含む最初のページ。
  5. リソースを読み込みます。 ページを一番下までスクロールして、すべての画像を読み込みます。
  6. PDF を生成します。 Puppeteer の Page.pdf() メソッドはそれを成功させます。
  7. 完了!

これが starlight-to-pdf の仕組みです。このパターンに従って、PDF エクスポート機能が欠けている他のドキュメント フレームワーク用に同様のツールを構築できます。

次のステップ

基本的な機能の準備ができたら、いくつかの追加機能を追加します。以下は、最も興味深く、やりがいのある機能です。

ヘッダーとフッターの追加

ヘッダーまたはフッターにページ番号と追加情報があると便利です。 Puppeteer の Page.pdf() メソッドは、headerTemplate オプションと footerTemplate オプションを受け入れます。これらのオプションは HTML 文字列を受け入れます。 Puppeteer は、特定のユーティリティ クラスを持つ要素に値を自動的に挿入します。

  • .date: フォーマットされた日付;
  • .title: Web ページの タグ値;</li> <li> .url: 印刷機能が呼び出されたページの URL;</li> <li> .pageNumber: 現在のページ番号;</li> <li> .totalPages: ドキュメント内の総ページ数。</li> </ul> <p>印刷前にすべてのコンテンツを 1 ページに結合するため、タイトルと URL にはあまり意味がありません。挿入された値は常に同じままです。ただし、他のクラスは非常に役立ちます。フッター テンプレートの例を次に示します:<br> </p> <pre class="brush:php;toolbar:false"><style> .footer-container { --color: #000; display: flex; align-items: center; justify-content: space-between; border-block-start: 1px solid var(--color); color: var(--color); font-size: 10px; font-family: Arial, Helvetica, sans-serif; margin-inline: 1.5cm 1cm; padding-block: 0.25cm 0.5cm; width: 100%; } </style> <div> <p>To use this, do not forget to set the displayHeaderFooter property to true:<br> </p> <pre class="brush:php;toolbar:false">import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://someUrl'); const footerTemplateStr = '<style>...<style><div>...</div>' // replace with the HTML string from the example above await page.pdf({ displayHeaderFooter: true, footerTemplate: footerTemplateStr }) </pre> <p>心に留めておくべきいくつかの発見事項があります:</p> </pre> <ul> <li>テンプレートは有効な HTML 構造である必要があります。</li> <li>Puppeteer のデフォルト値が 0 であるように、CSS のフォント サイズ プロパティを定義します。</li> <li>インライン を使用します。タグを使用してスタイルを定義します。ウェブサイトのスタイルはテンプレート内では使用できません。</li> <li>画像はbase64文字列としてエンコードする必要があります。</li> <li>希望のレイアウトを実現するには、Puppeteer のマージン オプションを使用します。</li> </ul> <h3> CLI スタイルについてはどうですか? </h3> <p>すべてが正常に動作し、結果として得られる PDF は見栄えがしますが、端末のメッセージは味気ないように感じられます。細部へのこだわりが良いものと素晴らしいものを分けますね。メッセージをもっとカラフルにして読みやすくしましょう。</p> <p>ANSI エスケープ シーケンスの魔法がここにあります。この仕事には 4 ビット カラーで十分であると判断しました。赤い背景に白いテキストを表示したいとします <em> (これは、エラー メッセージの前の [ERROR]: プレフィックスに使用したものです)</em>。この外観を実現する方法は次のとおりです:<br> </p> <pre class="brush:php;toolbar:false">console.log('\x1b[37;41m', 'White on red message'); </pre> <p>詳しく見てみましょう:</p> <ul> <li> x1b[ は 16 進数のエスケープ コードです (Unicode の代替として u001b を使用することもできます);</li> <li> 37 は前景の白色で、3 は前景、7 は白色を表します。</li> <li> 41 は背景の赤色で、4 は背景、1 は赤色を表します。</li> </ul> <p>すべてが機能していますが、console.log() 出力はすべてこの方法でスタイル設定されます。スタイルをデフォルトにリセットするには、最後にリセット シーケンス x1b[0m:<br> を追加するだけです。 </p> <pre class="brush:php;toolbar:false">console.log('\x1b[37;41m', 'White on red message', '\x1b[0m'); </pre> <p>ずっと良くなりました。灰色の背景 <em> (4 ビット色の名前では明るい黒) </em> に太字のシアンのテキストが必要な場合はどうすればよいでしょうか?簡単です:<br> </p> <pre class="brush:php;toolbar:false">console.log('\x1b[1;36;100m', 'Cyan on gray message in bold', '\x1b[0m'); </pre> <p>各部分の機能は次のとおりです:</p> <ul> <li> 1 エスケープ コードが太字効果を適用した後;</li> <li> 36 テキストの色をシアンに設定します。</li> <li> 100 は背景を明るい黒色に変更します。10 は <em>明るい</em> を意味し、0 は <em>黒</em> を表すコードです。</li> </ul> <p>この知識を使用すると、CLI ツールを視覚的に魅力的なものにすることができます。たとえば、プロジェクトではすべての URL とファイル パスを下線付きの青いテキストとしてスタイル設定しました。<br> </p> <pre class="brush:php;toolbar:false">console.log('\x1b[4;34m', './underlined/blue', '\x1b[0m') </pre> <p>このトピックの詳細については、このチートシートを確認してください。</p> <h2> まとめ </h2> <p>日常的なタスクが、いつやりがいのあるサイド プロジェクトのきっかけとなるかはわかりません。 starlight-to-pdf の開発により、Puppeteer と CLI スタイリングに関する貴重な経験が得られ、オープンソース コミュニティに新しいツールが登場しました。以下に簡単なデモンストレーションを示します:</p> <p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/173690789360353.jpg" class="lazy" alt="Transforming Starlight into PDF: experience and insights"></p> <p>以上がStarlight の PDF への変換: 経験と洞察の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。</p></div><div class="nphpQianMsg"><a href="javascript:void(0);">css</a> <a href="javascript:void(0);">html</a> <a href="javascript:void(0);">Static</a> <a href="javascript:void(0);">Array</a> <a href="javascript:void(0);">define</a> <a href="javascript:void(0);">if</a> <a href="javascript:void(0);">for</a> <a href="javascript:void(0);">date</a> <a href="javascript:void(0);">Error</a> <a href="javascript:void(0);">break</a> <a href="javascript:void(0);">using</a> <a href="javascript:void(0);">Property</a> <a href="javascript:void(0);">console</a> <a href="javascript:void(0);">number</a> <a href="javascript:void(0);">function</a> <a href="javascript:void(0);">default</a> <a href="javascript:void(0);">this</a> <a href="javascript:void(0);">margin</a> <a href="javascript:void(0);">background</a> <a href="javascript:void(0);">table</a> <a href="javascript:void(0);">everything</a> <a href="javascript:void(0);">Other</a><div class="clear"></div></div><div class="nphpQianSheng"><span>声明:</span><div>この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。</div></div></div><div class="nphpSytBox"><span>前の記事:<a class="dBlack" title="Node.js と TypeScript での LRU キャッシュの使用" href="https://m.php.cn/ja/faq/1796753817.html">Node.js と TypeScript での LRU キャッシュの使用</a></span><span>次の記事:<a class="dBlack" title="Node.js と TypeScript での LRU キャッシュの使用" href="https://m.php.cn/ja/faq/1796753834.html">Node.js と TypeScript での LRU キャッシュの使用</a></span></div><div class="nphpSytBox2"><div class="nphpZbktTitle"><h2>関連記事</h2><em><a href="https://m.php.cn/ja/article.html" class="bBlack"><i>続きを見る</i><b></b></a></em><div class="clear"></div></div><ins class="adsbygoogle" style="display:block" data-ad-format="fluid" data-ad-layout-key="-6t+ed+2i-1n-4w" data-ad-client="ca-pub-5902227090019525" data-ad-slot="8966999616"></ins><script> (adsbygoogle = window.adsbygoogle || []).push({}); </script><ul class="nphpXgwzList"><li><b></b><a href="https://m.php.cn/ja/faq/1609.html" title="Bootstrap リスト グループ コンポーネントの詳細な分析" class="aBlack">Bootstrap リスト グループ コンポーネントの詳細な分析</a><div class="clear"></div></li><li><b></b><a href="https://m.php.cn/ja/faq/1640.html" title="JavaScript関数のカリー化の詳細説明" class="aBlack">JavaScript関数のカリー化の詳細説明</a><div class="clear"></div></li><li><b></b><a href="https://m.php.cn/ja/faq/1949.html" title="JS パスワードの生成と強度検出の完全な例 (デモ ソース コードのダウンロード付き)" class="aBlack">JS パスワードの生成と強度検出の完全な例 (デモ ソース コードのダウンロード付き)</a><div class="clear"></div></li><li><b></b><a href="https://m.php.cn/ja/faq/2248.html" title="Angularjs は WeChat UI (weui) を統合します" class="aBlack">Angularjs は WeChat UI (weui) を統合します</a><div class="clear"></div></li><li><b></b><a href="https://m.php.cn/ja/faq/2351.html" title="JavaScript を使用して繁体字中国語と簡体字中国語をすばやく切り替える方法と、簡体字中国語と繁体字中国語の切り替えをサポートする Web サイトのトリック_javascript スキル" class="aBlack">JavaScript を使用して繁体字中国語と簡体字中国語をすばやく切り替える方法と、簡体字中国語と繁体字中国語の切り替えをサポートする Web サイトのトリック_javascript スキル</a><div class="clear"></div></li></ul></div></div><ins class="adsbygoogle" style="display:block" data-ad-format="autorelaxed" data-ad-client="ca-pub-5902227090019525" data-ad-slot="5027754603"></ins><script> (adsbygoogle = window.adsbygoogle || []).push({}); </script><footer><div class="footer"><div class="footertop"><img src="/static/imghwm/logo.png" alt=""><p>福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!</p></div><div class="footermid"><a href="https://m.php.cn/ja/about/us.html">私たちについて</a><a href="https://m.php.cn/ja/about/disclaimer.html">免責事項</a><a href="https://m.php.cn/ja/update/article_0_1.html">Sitemap</a></div><div class="footerbottom"><p> © php.cn All rights reserved </p></div></div></footer><script>isLogin = 0;</script><script type="text/javascript" src="/static/layui/layui.js"></script><script type="text/javascript" src="/static/js/global.js?4.9.47"></script></div><script src="https://vdse.bdstatic.com//search-video.v1.min.js"></script><link rel='stylesheet' id='_main-css' href='/static/css/viewer.min.css' type='text/css' media='all'/><script type='text/javascript' src='/static/js/viewer.min.js?1'></script><script type='text/javascript' src='/static/js/jquery-viewer.min.js'></script><script>jQuery.fn.wait = function (func, times, interval) { var _times = times || -1, //100次 _interval = interval || 20, //20毫秒每次 _self = this, _selector = this.selector, //选择器 _iIntervalID; //定时器id if( this.length ){ //如果已经获取到了,就直接执行函数 func && func.call(this); } else { _iIntervalID = setInterval(function() { if(!_times) { //是0就退出 clearInterval(_iIntervalID); } _times <= 0 || _times--; //如果是正数就 -- _self = $(_selector); //再次选择 if( _self.length ) { //判断是否取到 func && func.call(_self); clearInterval(_iIntervalID); } }, _interval); } return this; } $("table.syntaxhighlighter").wait(function() { $('table.syntaxhighlighter').append("<p class='cnblogs_code_footer'><span class='cnblogs_code_footer_icon'></span></p>"); }); $(document).on("click", ".cnblogs_code_footer",function(){ $(this).parents('table.syntaxhighlighter').css('display','inline-table');$(this).hide(); }); $('.nphpQianCont').viewer({navbar:true,title:false,toolbar:false,movable:false,viewed:function(){$('img').click(function(){$('.viewer-close').trigger('click');});}}); </script></body><!-- Matomo --><script> var _paq = window._paq = window._paq || []; /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="https://tongji.php.cn/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '9']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script><!-- End Matomo Code --></html>