トップへ(mam-mam.net/)

Delphi×PDFiumでPDFからWEBカタログ自動生成|画像変換対応

Delphi×PDFiumでPDFからWEBカタログ自動生成|画像変換対応

全ページ同じサイズのPDFファイルを指定するだけで、WEBカタログが自動生成できる仕組みをDelphiで構築できます。
このページでは、PDFiumを使ってPDFから画像変換を行い、それらをHTML形式で出力するWEBカタログ生成手法を紹介します。
DLLとライブラリの導入、VCLフォームの構成、ページごとの画像保存処理まで、構造的に整理されたサンプルコード付きで解説しています。

DelphiでPDFiumを使用する基本的な方法は https://mam-mam.net/delphi/vcl_pdfium.html を参照してください。
出来上がったアプリケーションは、https://mam-mam.net/download/web-digital-catalog/からダウンロードできます。

①DLLファイルのダウンロード

DelphiでPDFiumを使う方法|PDF表示・テキスト抽出・画像変換のサンプル付き解説
を参考に「pdfium.dll」ファイルを用意します。

②ライブラリファイルのダウンロード

DelphiでPDFiumを使う方法|PDF表示・テキスト抽出・画像変換のサンプル付き解説
を参考に「PdfiumCore.pas」「PdfiumCtrl.pas」「PdfiumLib.pas」ファイルを用意します。

プロジェクトの作成

Delphiを起動し[ファイル]⇒[新規作成]⇒[VCL フォーム アプリケーション -Delphi]をクリックします。
フォームに
TLabelをドラッグ&ドロップ(Label1)し、Captionプロパティを「タイトル:」にします。
TEditをドラッグ&ドロップ(Edit1)し、Textプロパティを「デジタルカタログ」にします。
TLabelをドラッグ&ドロップ(Label2)し、Captionプロパティを「ディスクリプション:」にします。
TEditをドラッグ&ドロップ(Edit2)し、Textプロパティを「デジタルカタログです。」にします。

TOpenDialogをドラッグ&ドロップ(OpenDilaog1)し、Filterプロパティを「*.pdf|*.pdf」に設定します。

TButtonをドラッグ&ドロップ(Button1)し、Captionプロパティを「デジタルカタログ作成」に設定します。
TPanelをドラッグ&ドロップ(Panel1)します。

TMemoをドラッグ&ドロップ(Memo1)し、Alignプロパティを「alBottom」、ScrollBarsプロパティを「ssBoth」に設定します。
TMemoをドラッグ&ドロップ(Memo2)し、ScrollBarsプロパティを「ssBoth」に設定し、Linesプロパティを以下に設定します。(とても長いです)

<html lang="ja">
<head>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <meta http-equiv="content-language" content="ja">
  <meta charset="UTF-8">
  <title>%title%</title>
  <meta name="Description" content="%description%">
<style>
html, body { height:100%; box-sizing:border-box;}
*, *::before, *::after { box-sizing:inherit;}
body{
  margin:0; padding:0; display:flex; flex-direction:column; font-size:20px;
  width:100%; max-width:100%; height:100%; max-height:100%; min-height:100%;
  font-family:"游ゴシック",YuGothic,"Hiragino Kaku Gothic ProN","Hiragino Sans","Meiryo UI","メイリオ",Meiryo,
              "MS Pゴシック","Helvetica Neue",Arial,sans-serif;
  background:#FFFFFF;
}
.view{
  margin:0; padding:0; width:100%; height:calc(100% - 40px - 40px); display:flex; flex-wrap:nowrap;
}
  .view>.left{
    margin:0; padding:0; height:100%; width:16px; display:flex;
    align-items:center; justify-content:center; flex-wrap:wrap; background:none;
  }
  .view>.center{
    margin:0; padding:8px; width:calc(100% - 16px * 2);
    max-width:100%; height:100%; overflow:hidden; position:relative;
  }
    .view>.center>.imgs{
      margin:0; padding:0; position:relative; transform-style: preserve-3d;
      perspective:300vw; cursor:grab;
    }

  .view>.right{
    margin:0; padding:0; width:16px; display:flex; align-items:center;
    justify-content:center; flex-wrap:wrap; background:none;
  }
.control{
  margin:0; width:100%; height:80px; display:flex; flex-direction:column;
  justify-content:center; background:none;
}
  .control>div:nth-child(1){
    margin:0; width:100%; height:40px; display:flex; align-items:center;
    justify-content:center;
  }
    .control>div:nth-child(1)>#page{
      height:100%; margin:0; padding:2px 8px; font-size:calc(28px  - 2px * 2);
    }
  .control>div:nth-child(2){
    margin:0; width:100%; height:40px; display:flex; align-items:center; justify-content:center;
  }

  .button-circle{
    display:inline-block; margin:2px; padding:2px; height:32px;
    width:32px; color:#000; text-shadow:1px 1px 2px #999;
    background:linear-gradient(#FFF, #CCC); border:none; text-decoration:none;
    box-shadow: 0px 0px 4px 2px rgba(0,0,0,0.2); cursor:pointer;
    border-radius:50% 50% 50% 50%; vertical-align:middle; user-select: none; font-size:0;
  }
  .button-circle:active{
    box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.2) inset; background:linear-gradient(#EEE, #BBB);
  }
  .button-circle:hover{
    background:linear-gradient(#FFF, #DDD);
  }

  .button-pdf{
    font-size:14px; color:#666; position:relative;
  }
</style>
<script>
  let catalog;

  class TCatalog{
    constructor(){
      this.urls=[%urls%];

      this.imgs=[];
      this.imgsDiv=[];
      this.page=0;
      this.baseZ=1000;
      this.paging=false;
      this.zoom={x:0, y:0, z:1.0, elm:document.querySelector(".view>.center>.imgs"), parent:document.querySelector(".view>.center")};
      this.ani=null;  //animateオブジェクトを入れる
      this.drag={isDown:false, isDrag:false, x:0, y:0};
      for(let i=0;i<this.urls.length;i++){
        this.imgs[i]=new Image();
        this.imgs[i].setAttribute("draggable","false");
      }
      let t=this.page+3;
      if(t>=this.urls.length){t=this.urls.length-1;}
      let sub=this.urls.slice(this.page,t);
      Promise.all(
        sub.map(function(url){
          return new Promise(function(resolve,reject){
            let img=new Image();
            img.onload=function(){resolve(img);}
            img.onerror=function(e){reject(e);}
            img.src=url;
          });
        })
      ).then(function(imgs){
        imgs.forEach(function(img,i){
          this.imgs[i]=img;
          this.imgs[i].setAttribute("draggable","false");
        }.bind(this));

        this.imgW=this.imgs[0].naturalWidth;
        this.imgH=this.imgs[0].naturalHeight;
        this.updateViewportState();
        this.setChange();
        window.addEventListener("resize",function(){
           requestAnimationFrame(function(){
             this.updateViewportState();
             this.setChange();
           }.bind(this));
        }.bind(this));
        document.querySelectorAll(".first-page").forEach(function(elm,idx,arr){
          elm.addEventListener("click",this.gotoFirst.bind(this));
        }.bind(this));
        document.querySelectorAll(".previous-page").forEach(function(elm,idx,arr){
          elm.addEventListener("click",this.gotoPrevious.bind(this));
        }.bind(this));
        document.querySelectorAll(".next-page").forEach(function(elm,idx,arr){
          elm.addEventListener("click",this.gotoNext.bind(this));
        }.bind(this));
        document.querySelectorAll(".last-page").forEach(function(elm,idx,arr){
          elm.addEventListener("click",this.gotoLast.bind(this));
        }.bind(this));
        document.querySelectorAll(".zoom-in").forEach(function(elm,idx,arr){
          elm.addEventListener("click",this.gotoZoomIn.bind(this));
        }.bind(this));
        document.querySelectorAll(".zoom-out").forEach(function(elm,idx,arr){
          elm.addEventListener("click",this.gotoZoomOut.bind(this));
        }.bind(this));
        this.zoom.parent.addEventListener("mousedown",function(e){
          this.drag.isDown=true;
          this.drag.x=e.clientX;
          this.drag.y=e.clientY;
        }.bind(this));
        this.zoom.parent.addEventListener("mousemove",function(e){
          if(this.drag.isDown && !this.drag.isDrag){
            if(Math.abs(this.drag.x-e.clientX)>2 || Math.abs(this.drag.y-e.clientY)>2){
              this.drag.isDrag=true;
            }
          }
          if(this.drag.isDrag){
            this.zoom.x += this.drag.x-e.clientX;
            this.zoom.y += this.drag.y-e.clientY;
            this.zoom.x = this.zoom.x<0 ? 0 : this.zoom.x;
            this.zoom.x = this.zoom.x>(this.zoom.parent.scrollWidth - this.zoom.parent.clientWidth) ? (this.zoom.parent.scrollWidth - this.zoom.parent.clientWidth) : this.zoom.x;
            this.zoom.y = this.zoom.y<0 ? 0 : this.zoom.y;
            this.zoom.y = this.zoom.y>(this.zoom.parent.scrollHeight - this.zoom.parent.clientHeight) ? (this.zoom.parent.scrollHeight - this.zoom.parent.clientHeight) : this.zoom.y;
            this.zoom.parent.scrollLeft = this.zoom.x;
            this.zoom.parent.scrollTop  = this.zoom.y;
            this.drag.x = e.clientX;
            this.drag.y = e.clientY;
          }
        }.bind(this));
        this.zoom.parent.addEventListener("mouseup",function(e){
          this.drag.isDrag=false;
          this.drag.isDown=false;
        }.bind(this));
        this.zoom.parent.addEventListener("touchstart",function(e){
          e.preventDefault();
          let t = e.touches[0];
          this.drag.isDown=true;
          this.drag.x = t.clientX;
          this.drag.y = t.clientY;
        }.bind(this), {passive:false});
        this.zoom.parent.addEventListener("touchmove", function(e){
          e.preventDefault();
          let t = e.touches[0];
          if(this.drag.isDown && !this.drag.isDrag){
            if(Math.abs(this.drag.x-t.clientX)>2 || Math.abs(this.drag.y-t.clientY)>2){
              this.drag.isDrag=true;
            }
          }
          if (this.drag.isDrag) {
            this.zoom.x += this.drag.x - t.clientX;
            this.zoom.y += this.drag.y - t.clientY;
            this.zoom.x = this.zoom.x<0 ? 0 : this.zoom.x;
            this.zoom.x = this.zoom.x>(this.zoom.parent.scrollWidth - this.zoom.parent.clientWidth) ? (this.zoom.parent.scrollWidth - this.zoom.parent.clientWidth) : this.zoom.x;
            this.zoom.y = this.zoom.y<0 ? 0 : this.zoom.y;
            this.zoom.y = this.zoom.y>(this.zoom.parent.scrollHeight - this.zoom.parent.clientHeight) ? (this.zoom.parent.scrollHeight - this.zoom.parent.clientHeight) : this.zoom.y;
            this.zoom.parent.scrollLeft = this.zoom.x;
            this.zoom.parent.scrollTop  = this.zoom.y;
            this.drag.x = t.clientX;
            this.drag.y = t.clientY;
          }
        }.bind(this), {passive:false});
        this.zoom.parent.addEventListener("touchend", function(e){
          this.drag.isDrag = false;
          this.drag.isDown=false;
        }.bind(this));
        window.addEventListener("mouseup",function(){
          this.drag.isDrag=false;
          this.drag.isDown=false;
        }.bind(this));
        window.addEventListener("touchend",function(){
          this.drag.isDrag=false;
          this.drag.isDown=false;
        }.bind(this));
      }.bind(this)).catch(function(e){
        //1つでも画像ファイルがロードできなければエラー
        console.log('Error:ロードできない画像有り');
      });
    }

    //画像の再構成
    setChange(){
      this.zoom.elm.innerHTML="";
      this.imgsDiv=[];
      if(this.sp){
        //スマホの場合
        for(let i=0;i<this.imgs.length;i++){
          this.imgsDiv[i]=document.createElement("div");
          this.imgsDiv[i].style.cssText="width:100%;height:100%;position:absolute;box-shadow:3px 4px 4px 0px rgba(0,0,0,0.2);z-index:"+this.baseZ+i+";";
          //this.imgsDiv[i].style.filter = "drop-shadow(4px 4px 4px rgba(0,0,0,0.2))";
          if(i<=this.page){
            this.imgsDiv[i].style.left="0%";
          }else{
            this.imgsDiv[i].style.left="100%";
          }
          if (Math.abs(i - this.page) <= 2) {
            this.imgsDiv[i].style.display = "block";
          }else{
            this.imgsDiv[i].style.display = "none";
          }
          this.zoom.elm.appendChild(this.imgsDiv[i]);
          this.imgs[i].style.cssText="width:100%;height:100%;position:absolute;";
          this.imgsDiv[i].appendChild(this.imgs[i]);
        }
        this.zoom.elm.style.overflow="hidden";
      }else{
        let j=0;
        for(let i=0;i<this.imgs.length;i++){
          if(i%2===0){
            j=this.imgsDiv.length;
            this.imgsDiv[j]=document.createElement("div");
            this.imgsDiv[j].style.cssText="width:50%;height:100%;position:absolute;transform-style:preserve-3d;transform-origin:right center;box-shadow:-3px 4px 6px 0px rgba(0,0,0,0.2);";

            if (Math.abs(j - Math.floor(this.page / 2)) <= 2) {
              this.imgsDiv[j].style.display = "block";
            }else{
              this.imgsDiv[j].style.display = "none";
            }
            this.zoom.elm.appendChild(this.imgsDiv[j]);
            this.imgs[i].style.cssText="width:100%;height:100%;position:absolute;transform:rotateY(180deg);backface-visibility:hidden;user-select:none;";
            if( i<this.page ){
              this.imgsDiv[j].style.transform="rotateY(0deg)";
              this.imgsDiv[j].style.zIndex=this.baseZ-(this.page-i);
            }else{
              this.imgsDiv[j].style.transform="rotateY(180deg)";
              this.imgsDiv[j].style.zIndex=this.baseZ+(this.page-i);
            }
          }else{
            this.imgs[i].style.cssText="width:100%;height:100%;position:absolute;transform:rotateY(0deg);backface-visibility:hidden;user-select:none;";
          }
          this.imgsDiv[j].appendChild(this.imgs[i]);
        }
        this.zoom.elm.style.overflow="visible";
      }
    }
    updateZIndexPC(flag=false){
      let n=Math.floor((this.page+1)/2);
      if(flag){n--;}
      if(n<0){n=0;}
      for(let i=0;i<this.imgsDiv.length;i++){
        if(i<=n){
          this.imgsDiv[i].style.zIndex=this.baseZ-(n-i);
        }else{
          this.imgsDiv[i].style.zIndex=this.baseZ-(i-n);
        }
      }
    }
    //this.zoom.elm(.view>.center>.imgs)のサイズ調整
    updateViewportState(){
      //親のパディングを引いた幅を求める・・・box-sizing:border-boxの為
      let sty=window.getComputedStyle(this.zoom.parent);
      this.zoom.parentW=parseFloat(sty.width)-parseFloat(sty.paddingLeft)-parseFloat(sty.paddingRight);
      this.zoom.parentH=parseFloat(sty.height)-parseFloat(sty.paddingTop)-parseFloat(sty.paddingBottom);
      if(this.zoom.parentW>=this.zoom.parentH){
        this.sp=false;
      }else{
        this.sp=true;
      }
      if(this.sp){
        // this.imgW:this.imgH = this.zoom.parentW:this.zoom.parentH
        //の縦横比で高さ、幅のどちらに合わせるか決める
        this.zoom.elm.style.boxShadow="4px 4px 4px 0px rgba(0,0,0,0.3)";
        if(this.imgW/this.imgH >= this.zoom.parentW/this.zoom.parentH){
          //子の幅の割合が親の幅の割合より大きいから親の幅で決める
          this.zoom.elm.style.width=(this.zoom.parentW*this.zoom.z)+"px";
          this.zoom.elm.style.left="0px";
          this.zoom.elm.style.height=this.imgH*this.zoom.parentW/(this.imgW)*this.zoom.z+"px";
          let t=(this.zoom.parentH-parseFloat(this.zoom.elm.style.height))/2;
          if(t<0){t=0;}
          this.zoom.elm.style.top=t+"px";
        }else{
          this.zoom.elm.style.height=(this.zoom.parentH*this.zoom.z)+"px";
          this.zoom.elm.style.top="0px";
          this.zoom.elm.style.width=(this.imgW*this.zoom.parentH/this.imgH)*this.zoom.z+"px";
          let l=(this.zoom.parentW-parseFloat(this.zoom.elm.style.width))/2;
          if(l<0){l=0;}
          this.zoom.elm.style.left=l+"px";
        }
      }else{
        // this.imgW*2:this.imgH = this.zoom.parentW:this.zoom.parentH の縦横比で高さ、幅のどちらに合わせるか決める
        this.zoom.elm.style.boxShadow="";
        if(this.imgW*2/this.imgH >= this.zoom.parentW/this.zoom.parentH){
          //子の幅の割合が親の幅の割合より大きいから親の幅で決める
          this.zoom.elm.style.width=(this.zoom.parentW*this.zoom.z)+"px";
          this.zoom.elm.style.left="0";
          this.zoom.elm.style.height=this.imgH*this.zoom.parentW/(this.imgW*2)*this.zoom.z+"px";
          let t=(this.zoom.parentH-parseFloat(this.zoom.elm.style.height))/2;
          if(t<0){t=0;}
          this.zoom.elm.style.top=t+"px";
        }else{
          this.zoom.elm.style.height=(this.zoom.parentH*this.zoom.z)+"px";
          this.zoom.elm.style.top="0";
          this.zoom.elm.style.width=(this.imgW*2*this.zoom.parentH/this.imgH)*this.zoom.z+"px";
          let l=(this.zoom.parentW-parseFloat(this.zoom.elm.style.width))/2;
          if(l<0){l=0;}
          this.zoom.elm.style.left=l+"px";
        }
      }
      this.zoom.parent.scrollLeft=this.zoom.x;
      this.zoom.parent.scrollTop=this.zoom.y;
      document.querySelector("#page").innerHTML= (this.page+1)+"/"+this.imgs.length;
    }

    gotoZoomIn(){
      let sc=window.getComputedStyle(this.zoom.elm);
      let wc=parseFloat(sc.width)/ this.zoom.z;
      let hc=parseFloat(sc.height)/ this.zoom.z;
      let nz=this.zoom.z+0.5;
      if(nz>4.0){ nz=4.0; }
      if(nz==1.0){
        this.zoom.x=0;
        this.zoom.y=0;
      }else{
        this.zoom.x += wc*(nz-this.zoom.z)/2;
        this.zoom.y += hc*(nz-this.zoom.z)/2;
      }
      this.zoom.z=nz;
      this.updateViewportState();
    }

    gotoZoomOut(){
      let sc=window.getComputedStyle(this.zoom.elm);
      let wc=parseFloat(sc.width)/ this.zoom.z;
      let hc=parseFloat(sc.height)/ this.zoom.z;
      let nz=this.zoom.z-0.5;
      if(nz<1.0){ nz=1.0; }
      if(nz==1.0){
        this.zoom.x=0;
        this.zoom.y=0;
      }else{
        this.zoom.x += wc*(nz-this.zoom.z)/2;
        this.zoom.y += hc*(nz-this.zoom.z)/2;
      }
      this.zoom.z=nz;
      this.updateViewportState();
    }

    gotoPrevious(){
      if(this.paging){return;}
      if(this.page==0){return;}
      this.paging=true;
      this.zoom.x=0;
      this.zoom.y=0;
      this.zoom.z=1.0;
      this.updateViewportState();
      if(this.sp){  //■スマホ
        this.zoom.x=0;
        this.zoom.y=0;
        this.zoom.z=1.0;
        this.updateViewportState();
        this.ani=this.imgsDiv[this.page].animate(
          [  {left:"0%"}, {left:"100%"}  ],
          {
            delay:0, direction:'normal', duration:300, easing:'linear',
            endDelay:0, fill:'none', iterationStart:0.0, iterations:1,
          }
        );
        this.ani.onfinish=function(){
          this.imgsDiv[this.page+1].style.left="100%";
          this.paging=false;
        }.bind(this);
        this.page--;
        this.removeImgSrc();
      }else{    //■PC
        if(this.page % 2==1){
          this.page--;
        }else{
          this.page-=2;
        }
        if(this.page<0){this.page=0;}
        let aniDiv=Math.ceil(this.page/2);
        this.removeImgSrc();
        this.updateZIndexPC();
        this.ani=this.imgsDiv[aniDiv].animate(
          [  {transform:'rotateY(0deg)'},{transform:'rotateY(180deg)'}  ],
          {
            delay:0, direction:'normal', duration:300, easing: 'linear',
            endDelay:0, fill:'none', iterationStart: 0.0, iterations:1,
          }
        );
        this.ani.onfinish=function(aniDiv){
          this.imgsDiv[aniDiv].style.transform="rotateY(180deg)";
          this.paging=false;
        }.bind(this, aniDiv);
      }
      document.querySelector("#page").innerHTML= (this.page+1)+"/"+this.imgs.length;
    }
    gotoNext(){
      if(this.paging){return;}
      if(this.page>=(this.urls.length-1)){return;}
      this.paging=true;
      this.zoom.x=0;
      this.zoom.y=0;
      this.zoom.z=1.0;
      this.updateViewportState();
      if(this.sp){
        this.page++;
        if(this.page>(this.urls.length-1)){this.page=this.urls.length-1;}
        this.removeImgSrc();
        this.ani=this.imgsDiv[this.page].animate(
          [  {left:"100%"}, {left:"0%"}  ],
          {
            delay:0, direction:'normal', duration: 300, easing:'linear',
            endDelay:0, fill:'none', iterationStart:0.0, iterations: 1,
          }
        );
        this.ani.onfinish=function(){
          this.imgsDiv[this.page].style.left="0%";
          this.paging=false;
        }.bind(this);
      }else{  //PC
        if(this.page%2==1){
          this.page+=3;
        }else{
          this.page+=2;
        }
        if(this.page>(this.urls.length-1)){this.page=this.urls.length-1;}
        let aniDiv=Math.floor((this.page-1)/2);
        this.updateZIndexPC(true);
        this.ani=this.imgsDiv[aniDiv].animate(
          [  {transform:'rotateY(180deg)'},{transform:'rotateY(0deg)'}  ],
          {
            delay:0, direction:'normal', duration:300, easing:'linear',
            endDelay:0, fill:'none', iterationStart:0.0, iterations: 1,
          }
        );
        this.ani.onfinish=function(aniDiv){
          this.imgsDiv[aniDiv].style.transform="rotateY(0deg)";
          this.paging=false;
        }.bind(this, aniDiv);
        this.removeImgSrc();
      }
      document.querySelector("#page").innerHTML= (this.page+1)+"/"+this.imgs.length;
    }
    gotoLast(){
      if(this.paging){return;}
      if(this.page>=(this.urls.length-1)){return;}
      this.goto(this.urls.length-1);
    }
    gotoFirst(){
      if(this.paging){return;}
      if(this.page<=0){return;}
      this.goto(0);
    }
    goto(pg){
      if(this.paging){return;}
      this.zoom.x=0;
      this.zoom.y=0;
      this.zoom.z=1.0;
      this.page=pg;
      this.updateViewportState();
      this.removeImgSrc();
      if(this.sp){
        for(let i=0;i<this.imgsDiv.length;i++){
          if(i<=pg){
            this.imgsDiv[i].style.left="0%";
          }else{
            this.imgsDiv[i].style.left="100%";
          }
        }
      }else{
        let newDiv=Math.floor((this.page-1)/2);
        this.updateZIndexPC();
        for(let i=0;i<this.imgsDiv.length;i++){
          if(i<=newDiv){
            this.imgsDiv[i].style.transform="rotateY(0deg)";
          }else{
            this.imgsDiv[i].style.transform="rotateY(180deg)";
          }
          if(Math.abs(i - newDiv) <= 2) {
            this.imgsDiv[i].style.display = "block";
          }else{
            this.imgsDiv[i].style.display = "none";
          }
        }
      }
      document.querySelector("#page").innerHTML= (this.page+1)+"/"+this.imgs.length;
    }
    //現在ページの前後3ページ以外のソースを消す
    removeImgSrc(){
      for(let i=0;i<this.imgs.length;i++){
        if(Math.abs(i-this.page)<=3){
          this.imgs[i].src=this.urls[i];
        }else{
          this.imgs[i].src="";
        }
        if(this.sp){
          if (Math.abs(i - this.page) <= 2) {
            this.imgsDiv[i].style.display = "block";
          }else{
            this.imgsDiv[i].style.display = "none";
          }
        }else{
          let divPage=Math.floor(i/2);
          if (Math.abs(i - this.page) <= 4) {
            this.imgsDiv[divPage].style.display = "block";
          }else{
            this.imgsDiv[divPage].style.display = "none";
          }
        }
      }
    }
  }


  document.addEventListener("DOMContentLoaded",function(){
    catalog=new TCatalog();
  });
</script>
</head>
<body>
  <div class="view">
    <div class="left">
    </div>
    <div class="center">
      <div class="imgs"></div>
    </div>
    <div class="right">
    </div>
  </div>
  <div class="control">
    <div>
      <a class="button-circle first-page" title="最初へ">
        <svg style="width:28px;height:auto;" viewBox="0 0 64 64">
          <path d="M48,4 l0,56 l-32,-28 z" style="fill:#555;stroke:none;stroke-width:0;"/>
          <line style="fill:none;stroke:#555;stroke-width:6;" x1="16" y1="4" x2="16" y2="60"/>
        </svg>
      </a>
      <a class="button-circle previous-page" title="前へ">
        <svg style="width:28px;height:auto;" viewBox="0 0 64 64">
          <path d="M48,4 l0,56 l-40,-28 z" style="fill:#555;stroke:none;stroke-width:0;"/>
        </svg>
      </a>
      <div id="page" style="height:auto;align-self:center;"></div>
      <a class="button-circle next-page" title="次へ">
        <svg style="width:28px;height:auto;" viewBox="0 0 64 64">
          <path d="M16,4 l0,56 l40,-28 z" style="fill:#555;stroke:none;stroke-width:0;"/>
        </svg>
      </a>
      <a class="button-circle last-page" title="最後へ">
        <svg style="width:28px;height:auto;" viewBox="0 0 64 64">
          <path d="M16,4 l0,56 l32,-28 z" style="fill:#555;stroke:none;stroke-width:0;"/>
          <line style="fill:none;stroke:#555;stroke-width:6;" x1="48" y1="4" x2="48" y2="60"/>
        </svg>
      </a>
    </div>
    <div>
      <a class="button-circle zoom-in" title="拡大">
        <svg style="width:28px;height:auto;" viewBox="0 0 64 64">
          <circle style="fill:none;stroke:#555;stroke-width:6;" cx="24" cy="24" r="16" />
          <line style="fill:none;stroke:#555;stroke-width:6;" x1="36" y1="36" x2="52" y2="52" />
          <line style="fill:none;stroke:#555;stroke-width:4;" x1="14" y1="24" x2="34" y2="24" />
          <line style="fill:none;stroke:#555;stroke-width:4;" x1="24" y1="14" x2="24" y2="34" />
        </svg>
      </a>
      <a class="button-circle zoom-out" title="縮小">
        <svg style="width:28px;height:auto;" viewBox="0 0 64 64">
          <circle style="fill:none;stroke:#555;stroke-width:6;" cx="24" cy="24" r="16" />
          <line style="fill:none;stroke:#555;stroke-width:6;" x1="36" y1="36" x2="52" y2="52" />
          <line style="fill:none;stroke:#555;stroke-width:4;" x1="14" y1="24" x2="34" y2="24" />
        </svg>
      </a>
      <a class="button-circle button-pdf" href="catalog.pdf" target="_blank" title="PDF">
        <span>PDF</span>
      </a>
    </div>
  </div>
</body>
</html>


[ファイル]⇒[すべて保存]をクリックして、
「c:\Users\・・・\Embarcadero\Studio\Projects」に「VCL_PDFium_Catalog」フォルダを作成して、ユニットを「Unit1.pas」、プロジェクトを「Project1.dproj」として保存します。

「VCL_PDFium_Catalog」フォルダに「PdfiumCore.pas」「PdfiumCtrl.pas」「PdfiumLib.pas」ファイルをコピーします。
「VCL_PDFium_Catalog\win32\debug」フォルダに「pdfium-win-x86.tgz」を解凍した「bin」ディレクトリにある「pdfium.dll」ファイルをコピーします。

ソースコードの記述

IDEを「コード」に切り替えて(「F12」キーを押す)以下ソースコードを貼り付けます。

「デザイン」に切り替えて(「F12」キーを押す)、
「Form1のOnCreate」イベントプロパティに「FormCreate」を割り当て、
「Form1のOnDestroy」イベントプロパティに「FormDestroy」を割り当て、
「Button1のOnClick」イベントプロパティに「Button1Click」を割り当て、

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, StrUtils,
  Vcl.ExtCtrls, {Vcl.ComCtrls,} pdfiumcore, pdfiumCtrl;

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    Panel1: TPanel;
    Memo1: TMemo;
    Memo2: TMemo;
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private 宣言 }
    PdfControl:TPdfControl;
    FilePath:String;//実行ファイルのあるフォルダ
    Path:String;//画像作成時の年月日時分秒
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses winapi.shellapi, System.Win.Registry, Vcl.Imaging.pngimage;


//デフォルトブラウザのフルパスを取得する
function GetDefaultBrowserCommand: string;
var Reg: TRegistry;
    i:Integer;
begin
  Reg := TRegistry.Create(KEY_READ);
  try
    Reg.RootKey := HKEY_CLASSES_ROOT;
    if Reg.OpenKeyReadOnly('http\shell\open\command') then
      Result := Reg.ReadString('');
  finally
    Reg.Free;
  end;
  if Result<>'' then
  begin
    if LeftStr(Result,1)='"' then
    begin
      i := Pos('"', Result,2);
      Result:=Result.Substring(0,i);
    end;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var st, HtmlText:string;
    i:Integer;
    Rct:TRect;
    Bmp:TBitmap;
    Png:TPngImage;
    StrList,UrlList,TextList:TStringList;
    SettingWidth:Integer;
begin
  st:=OpenDialog1.FileName;
  if st<>'' then
  begin
    OpenDialog1.InitialDir:=ExtractFileDir(st);
    OpenDialog1.FileName:=ExtractFileName(st);
  end;
  if not OpenDialog1.Execute(self.Handle) then exit;

  Button1.Enabled:=False;
  SettingWidth:=1500;//幅1500pxで各ページを出力する

  Path:=FormatDateTime('yyyymmddhhnnss',now());
  CreateDir(FilePath+Path);
  CreateDir(FilePath+Path+'\imgs');
  Memo1.Clear;

  CopyFile(PChar(OpenDialog1.FileName),PChar(FilePath+Path+'\catalog.pdf'),False);

  PdfControl.LoadFromFile(OpenDialog1.FileName);
  Bmp:=TBitmap.Create;
  Png:=TPngImage.Create;
  UrlList:=TStringList.Create;
  TextList:=TStringList.Create;
  try
    for i := 0 to PdfControl.PageCount-1 do
    begin
      PdfControl.PageIndex:=i;
      Rct:=pdfcontrol.GetPageRect;
      Bmp.Width:=SettingWidth;
      Bmp.Height:=Rct.Height*SettingWidth div Rct.Width;
      PdfControl.Document.Pages[i].Draw(
        Bmp.Canvas.Handle,0,0, Bmp.Width, Bmp.Height
      );
      Bmp.PixelFormat:=pf24bit;
      Png.Assign(Bmp);
      Png.CompressionLevel:=7;
      Png.SaveToFile(FilePath+Path+'\imgs\'+Format('%.4d.png', [i]));
      Memo1.Lines.Add( '変換中 '+IntToStr(i+1)+'/'+IntToStr(PdfControl.PageCount)+':'+Format('%.4d.png', [i]) );
      UrlList.Add('./imgs/'+Format('%.4d.png', [i]));

      Application.ProcessMessages;
    end;
  finally
    Bmp.Free;
    Png.Free;
  end;

  st:='';
  for i := 0 to UrlList.Count-1 do
    st:=st+'"'+UrlList[i]+'"'+IfThen(i<(UrlList.Count-1),',');

  HtmlText:=Memo2.Text;
  HtmlText:=StringReplace(HtmlText,'%title%',Edit1.Text,[rfReplaceAll]);
  HtmlText:=StringReplace(HtmlText,'%description%',Edit2.Text,[rfReplaceAll]);
  HtmlText:=StringReplace(HtmlText,'%urls%',st,[rfReplaceAll]);

  StrList:=TStringList.Create;
  try
    StrList.Text:=HtmlText;
    StrList.SaveToFile(FilePath+Path+'\index.html',TEncoding.UTF8);
  finally
    StrList.Free;
  end;
  UrlList.Free;
  TextList.Free;
  Button1.Enabled:=True;

  Memo1.Lines.Add(FilePath+Path);
  Memo1.Lines.Add('デジタルカタログの作成完了');
  //ShellExecute(Handle, 'open', 'explorer.exe', PChar(FilePath+Path), nil, SW_SHOW);

  st:=GetDefaultBrowserCommand();
  ShellExecute(Handle, 'open', PChar(st), PChar('"file:///'+FilePath+Path+'\index.html"'), nil, SW_SHOW);
  PdfControl.Close;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  PDFiumDllDir := ExtractFileDir(Application.ExeName);
  PdfControl := TPdfControl.Create(Self);
  PdfControl.Parent := Panel1;
  FilePath:=ExtractFilePath(Application.ExeName);
  if RightStr(FilePath,1)<>'\' then Path:=Path+'\';
end;


procedure TForm1.FormDestroy(Sender: TObject);
begin
  PdfControl.Free;
end;


end.

実行する

実行して「Button1」をクリックすると、ファイル選択ダイアログが表示されるので、全て同じサイズのページで2ページ以上あるPDFファイルを選択します。
PDFファイルから文字列を抜き出し画像変換され、WEBブラウザで動作するデジタルカタログが生成されます。

作成したWEBデジタルカタログはこちら