Vue を使用して Web ページを PDF に変換する方法実装手順と問題解決:

実装手順: (vue の実装について知りたいだけの場合は、方法 1 を見る必要はなく、方法 2 だけを見てください)

方法 1:

node.js と puppeteer (Google 自動検出ツール) を使用して、最初の試みの結果が理想的ではないため、直接大まかに説明します: (元のコードは次のように実装されています。すべての依存パッケージが役立つわけではありません。Puppeteer は必要であり、npm によって導入する必要があります。)

const puppeteer = require('puppeteer');

const useProxy = require('puppeteer-page-proxy');

const {delay} = require("bluebird");

const Promise = require("bluebird");

const ms = require("ms");

const fs = require('fs');



(async () => {

    const browser = await puppeteer.launch();

    // const browser = await puppeteer.launch({headless:false});

    // const page = await browser.newPage();

    const page = await browser.newPage();

    await page.goto('http://127.0.0.1:5173/',

    {waitUntil:'networkidle2'}

    );

    // await delay(ms("5s"));

    await page.pdf({path: './test123.pdf' , format: 'A4',printBackground:true});

    await browser.close();

})();

実装原理は非常に単純です, つまり, puppeteer を通じて Web ページを自動的に開き, それからその pdf メソッドを呼び出してそれを保存します. printBackground: true パラメータが背景色の表示を制御することは注目に値します. true がそうでない場合を選択すると、背景色はレンダリングされません。この利点は、生成が簡単で、画像が非常に鮮明で、ズームインした後でも依然として鮮明で、メモリが多すぎないことです。 3 枚の写真は 3 メートルを超えており、生成されたファイルは 3.56 メートルであり、より一貫性があることがわかります。短所: ①生成されるPDFはデフォルトで2ページになっており、色々試しましたが学力不足の可能性もあり、PuppeteerのAPIドキュメントでは対応するパラメータ設定が見つからず、Baidu検索などで見つけました。良い解決策が見つかりませんでした。まだ不明です。理由は、他の Web サイトを試してみたところ、一部のページが完全に表示されなかったためです。具体的な理由は書きません。いずれにせよ、作者は行き詰まりに陥ったのかもしれません。何度も試してもダメだったので、Vue を使って解決しました。②新しいウィンドウを開く必要があるため、ジャンプ操作などが発生しますが、ヘッドレスモードもありますが、①に騙されて試していません。

方法 2:

メソッド参照元:

Vue から PDF を生成する方法 page_vue は pdf_を生成します Angry Little Sheep のブログ - CSDN ブログ

vue プロジェクトで、まず必要な 2 つの依存関係 (html2canvas と jspdf) をインストールします。

①npm install --save html2canvas

②npm install jspdf --save

①この機能は、最初に PDF に変換する必要がある HTML ページを Canvas に変換することです (canvas は HTML のタグであり、描画、特殊効果、さらにはミニゲームでも非常に重要です。理解するには、関連するリンクにアクセスしてください)一連の知識、詳細な説明はありません) をキャンバスに変換した後、一連のパラメーターを通じて変換する必要がある PDF を調整できます。デフォルトでは、生成されたキャンバスは非常にぼやけているため、これは非常に重要なポイントです。問題と解決策は次のとおりです。詳しくは後で説明します。

②の機能は、canvasから変換した画像を必要なpdfに変換してエクスポートするものですが、ここでできることは多くなく、比較的簡単です。

次に、具体的な実装手順を示します。 (ブロガーの「Angry Little Sheep」を参照してください。共有してくれてありがとうございます。まず、小さな羊のコードに基づいて実行できます。私のコード シナリオは彼のものとは異なるため、私のコードはサンプル コードではありません)もしかしたら実行できない場合もあるかもしれませんが、羊の方は可能です。試してみました。)

<template>

<button @click="handleExport">导出</button>

<div ref="pdf" class="spec">

需要转换成pdf的结构或者图片

</div>

</template>

次に、click関数を定義し、該当ページのpdfを取得してdownloadPDF関数に渡しますが、downloadPDF構造は高コストなので、抽出してpdf.jsに保存します。

const handleExport =()=>{

            console.log(proxy.$refs.pdf)

            downloadPDF(proxy.$refs.pdf)

        }

pdf.js には多くのコンテンツが含まれているため、js をインポートして抽出します。

import {downloadPDF} from "./pdf.js"

pdf.js内容如下:

import html2canvas from "html2canvas";

import jsPDF from "jspdf";

import compress from './compress.js';



function base64ToFile(dataURL) {

  var arr = dataURL?.split?.(',')

  let mime = arr[0].match(/:(.*?);/)[1]

  let bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);

  while (n--) {

    u8arr[n] = bstr.charCodeAt(n);

  }

  let filename = new Date().getTime() + "" + Math.ceil(Math.random() * 100) + "." + mime.split("/")[1]

  return (new File([u8arr], filename, { type: mime }))

}




export const downloadPDF = page => {

  html2canvas(page,{

    allowTaint: true, //开启跨域

    useCORS: true,

    scale: 2,

  }).then(function(canvas) {

    canvas2PDF(canvas);

  });

};

const canvas2PDF = canvas => {

  let contentWidth = canvas.width*0.2;

  let contentHeight = canvas.height*0.2;

  let imgHeight = contentHeight;

  let imgWidth = contentWidth;

  let pdf = new jsPDF("p", "pt");

  let sharePic

  sharePic = canvas.toDataURL("image/jpeg", 1)

  let fileba = base64ToFile(sharePic)

  compress(fileba)

    .then(res => {

      pdf.addImage(

        res.compressBase64,

        "JPEG",

        0,

        0,

        imgWidth,

        imgHeight

      );

      // console.log(pdf,999)

      pdf.save("导出.pdf");

    })

    .catch(err => {

    // error(err);

    });

};

この時点で、対応する HTML コードを PDF に変換できます。次に、私が遭遇した問題と解決策について説明します。

問題と解決策:

①印刷したPDFには画像部分がありません

最初に遭遇した問題は、印刷された PDF に写真がないことです。試してみたところ、nodejs+puppeteer で直接印刷できました。PDF への変換方法が異なると思います。おそらく、puppeteer はスクリーンショットに似ています (推測ポイント) view) を作成し、最初に Canvas に変換する Vue は画像リソースをダウンロードする必要があります。ダウンロードされたリソースにはすべてクロスドメインの問題があります。最初はプロキシを設定して実装したかったのです (クロスドメインの問題とプロキシの設定方法)詳細は後ほど説明します)、自分で見直してまとめて送信するのを手伝ってください)が、結果があまりスムーズではなく、私の操作に問題がある可能性があるため、検索したところ、画像をbase64形式に変換できることがわかりましたクロスドメインの問題を避けるために、 http://nomad-public.oss-cn-shanghai.aliyuncs.com/size_chart/34e4debd-a8ea-4730-acd2-8a58cc7c6b5d.jpg?time=1677729826618 をざっと観察してみたところ、タイムスタンプを追加し、画像の前に image.setAttribute("crossorigin", "anonymous"); を追加することでクロスドメインポリシーの解決策とされています。 Base64に:

const downloadImage = (imgsrc) => {//下载图片地址和图片名(下载部分代码已经被我删除了,这是上一个需求用到了,我在此基础魔改了一下)

        var image = new Image();

        // 解决跨域 Canvas 污染问题,

        image.setAttribute("crossorigin", "anonymous");

        image.onload = function () {

        var canvas = document.createElement("canvas");

        canvas.width = image.width;

        canvas.height = image.height;

        var context = canvas.getContext("2d");

        context.drawImage(image, 0, 0, image.width, image.height);

        var url = canvas.toDataURL("image/png"); //将图片格式转为base64

        // console.log(url,"base64")

        };

        image.src = imgsrc + '?time=' + Date.now();  //注意,这里是灵魂,否则依旧会产生跨域问题

        console.log(image.src,"image.src")

        return image.src

        }

この関数の機能は、URL を渡すことです。この関数は、base64 形式に変換された画像の URL を返します (base64 と Canvas についての理解が浅いため、概念が間違っている可能性があります)。それから、それを PDF に変換し、画像が表示できることがわかります。ちなみに、背景画像形式の場合は、テンプレート文字列を使用してテンプレートに動的形式で追加できます。 style="{'margin-top ':'0','背景画像':` url(${downloadImage('https://nomad-public.oss-cn-shanghai.aliyuncs.com/size_chart/929c3c47-e0b6-4765-bda9-5fb8c1c05191. jpg')})`}"、この方法で回避できます。次に、動的 CSS スタイルを設定します。これで、最初の問題、画像が PDF に表示されない問題が解決されます。

②生成されたPDFの解像度が低すぎる

PDF を印刷した後、PDF の解像度が低すぎてモザイクのようになっているという別の問題を発見したため、問題の解決策を探しました。 (実際にはCSSで比率を上げているのと同じだとよく見かけます) 試してみたところうまくいきましたが、同時に画像が非常に大きくなってしまいました A4サイズが条件なので用紙サイズを変更し、縮尺を 4 に増やしてから、フォントなどを増やします。鮮明度ははるかに高くなりますが、用紙に表示できるのはコンテンツの 4 分の 1 未満 (生成された PDF の左上隅) であるため、PDF 全体を表示することはできません。 A4 で表示されます。明らかにこれほど良くないので、別のパラメータ dpi を見つけました。パラメータ全体の説明は私の期待と非常に一致していますが、役に立ちません。何が問題なのかわかりません。 html2canvas のパラメータにそのようなパラメータが存在しないことがわかったので、バージョンの問題かもしれません つまり、この方法 (最も単純な方法) は機能しないことがわかったので、上司の指導の下、新しいアイデアを思いつきました、これは、htmlをキャンバスに変換するときにscale: 2を設定して、変換されたキャンバス描画ボードを大きくしてから、写真を撮った後、写真のサイズを小さくして鮮明さを高めます(実際には同じです) dpi実装としてのアイデアですが、dpiパラメータが無効であるためさらに面倒です)

html2canvas(page,{

    allowTaint: true, //开启跨域

    useCORS: true,

    scale: 2,

  }).then(function(canvas) {

    canvas2PDF(canvas);

  });

次に、このコード スニペットでは、画像のサイズを縮小します。この方法は、一般に、最初に拡大してから縮小するとして知られています。最初にキャンバス (画板) を拡大し、次に画像のサイズを縮小して、画像に変換した後、鮮明さを高めます。このステップは、「まず巨大な画板の下に必要な絵を描きます。画板は非常に大きいので、絵があまり鮮明でなくても、描き終わったら縮小します」という一般的な方法で理解できます。 A4用紙サイズに絵を描いているので、同じ面積の画素数は大幅に増えますが、巨大な画板を小さなA4用紙に圧縮しているので、当然画素数も高くなります(個人的な理解です)

const canvas2PDF = canvas => {

下面位置就是width*0.2

  let contentWidth = canvas.width*0.2;

  let contentHeight = canvas.height*0.2;

  let imgHeight = contentHeight;

  let imgWidth = contentWidth;

それでも鮮明度が低いことがわかり、次に 2 番目のアイデアを思いつきました。それは、HTML コードの構造を拡大することでした。たとえば、最初は 600 ピクセルのボックスに配置されていたものを 1200 ピクセルに変更しました。考え方は先ほどと同じで、一つは精度を高めるために製図板を増やすということですが、これは描画する際に時間を費やすことを意味しますので、当然製図板の自己発熱も大きくなります。元の 600px に縮小すると、ピクセルは確実に増加しますが、私の意見では、この 2 つのアイデアは非常に似ています。現在生成されている画像の解像度はすでに非常に高く、基本的に企業のニーズを満たすことができます。

③生成されたPDFファイルが大きすぎる

先ほどの方法では、HTMLコードのCSSスタイルを変更して明瞭度を高めるとサイズが大きくなるため、元の画像も拡大されてしまうため、単純に縮小してから縮小すると画像が非常に大きくなってしまいます。処理されていない場合、生成される PDF のサイズは jpeg に変換すると約 13 メートル、元の画像のサイズはわずか 3 メートルであり、この問題の大きさがわかります。この PDF ファイルの送信プロセスは多くのリソースを浪費します。それで研究を始めました。圧縮については、まずコード構造を最適化し、ファイル サイズをほとんど変更しませんでした。そこで、画像から始めました。受信した画像が約 20kb だったとき、生成された PDF はまだ約 3.8m でした。コード Canvas.toDataURL("image/ jpeg", 1) では、パラメーター 1 を 0.92 に変更しました。1 は画像が 100% 復元されることを意味し、0.92 はファイル サイズの明瞭さが犠牲になることを意味します。効果は非常に大きく、3.8 から減少します。メートルから 1 メートルまで、予想通りですが、写真の解像度が変更されました。これは明らかに東の壁を取り壊して西の壁を補うためです。上司も、解像度が比較的低いと言っていました。 , そして、1mまでできるだけ鮮明さを維持したいと思っています. 私の最初の理解では、それはファイルが鮮明であればあるほど、それは大きくなります. 市販のPDFを圧縮するソフトウェアも見たことがありますが、無料の場合有料の場合を除いて、ファイルサイズの削減と引き換えに解像度が犠牲になるので、最初は何をすればいいのかわからず、もう一度行きました。連れて行ってくれた上司にアドバイスを求めました。 「はい、解像度を落とさずに、必要なだけ画像を圧縮できます。そこで、上司が .js 圧縮方法を教えてくれたので、それを試しました。3M ファイルを 400kb に直接圧縮しました。コードが与えられていたので、上司から私に送られてきましたので、無断で公開することはありません。私が書いたコードと参考記事のコードはすべて掲載しています。同じようなニーズがある場合は、プライベートメッセージを送ってください。非公開で送信できます。共有していただきありがとうございます。方法としては、おそらくファイルを渡して画像をbase64に変換し、canvasで処理して、まだはっきりとは調べていない方法で縮小することになると思いますが、インターネットで公開コードを検索してみると、記事内で共有し追記させていただきます。

最終的なエフェクト表示:

特別な感謝: 貴重なご意見をくださった良き兄弟に感謝します。コードをコードブロックに修正しました。未熟なのでご容赦ください。その他、読みにくい点があればご指摘ください。

おすすめ

転載: blog.csdn.net/weixin_54515240/article/details/129319084