ボリューム カウンターについて

こちらのページでは、iText 7 で使用するボリューム ライセンスのカウントの仕組みに関するよく寄せられる質問と回答を掲載しています。

このページの掲載内容は、開発元である Apryse 社の公開する iText 製品に関する Volume Counter FAQs ページを基にしています。

重要!

iText 7.2 のリリースより、従来のライセンス体系が新しい Unified Licensing Mechanism に置き換えられ、ユーザーがより簡単に使用状況を確認できるようになり、イベント カウント機能が拡張されました。これにより、従来の itext-licensekey および itext-licensekey-volume 依存関係が、新たに licensing-base および licensing-remote 依存関係に置き換わり、ライセンス ライブラリのバージョン番号がバージョン 3.x から 4.x になりました。また、従来の XML ライセンスファイルから JSON 形式の新しいファイルに置き換わりました。
ライセンス キー ファイルが XML ファイルの場合、このページの iText 7.1.x と書かれた説明に従ってください。そして、JSON ライセンス キー ファイルの場合は、iText 7.2.x の説明を参照してください。

ボリューム カウントを有効にするにはどうすればよいですか?

特に何も有効にする必要はありません。有効なボリューム ライセンス ファイルがあり、iText ライセンス キーのボリューム依存性をロードしていれば、iText が自動的にこれを有効にしてくれます。ライセンス キー ライブラリとボリューム ライセンス キー ライブラリの両方が必要です。インストールの詳細については、ボリューム カウンターのインストールガイドを参照してください。
iText 7.1.11 では、ライセンス キーと iText ライセンス キー ボリューム ライブラリのバージョン 3.1 を導入し、「ボリューム サブスクリプション」ライセンスをお持ちの方は、ボリューム カウントをローカルで行うか、リモートで行うか (使用状況レポートのための最も便利な方法) を指定する必要がありました。iText 7.1.15 と License Key および iText License Key Volume ライブラリのバージョン 3.1.4 のリリースでは、リモート レポートがデフォルトで有効になっており、使用量のレポートがより簡単になりました。

インターネット接続に失敗した場合は?

iText がサーバーに接続できなくても、コードやプログラムに影響を受けませんのでご安心ください。
インターネット接続に失敗した場合、iText は実行中に失敗したリクエストをすべてキャッシュし、次のイベントで送信を試みます。リクエストを送信するためには、アプリケーションの現在のランタイム中に接続がある必要があります。アプリケーションがシャットダウンした場合、統計情報は引き継がれません。

ボリューム カウントのインターフェイスは、完全クローズド環境で使用しても問題ないですか?

ボリューム カウントを動作させるには、kinesis.us-east-1.amazonaws.com、cognito-identity.eu-central-1.amazonaws.com、sdk-requests.license.itextpdf.com、 sdk-login.licensing.itextpdf.com および sdk-health.licensing.itextpdf.com におけるボリューム カウント サーバーの 443 ポートへのアクセスを許可する必要があります。
ただし、情報は送信されるだけで受信されないため、アクセスは「一方通行」であることが求められます。

iText はどのようなデータを収集していますか?

iText のバージョンとその実行環境、ライセンスキー、イベントの種類 (どの製品のものか)、発生した時間などの情報を収集しています。これらの情報は、iText の開発、特定の機能の使用状況の把握、Java や .NET の対象バージョンの把握に役立ちます。
iText が収集する情報の詳細は、お客様のソフトウェア ライセンス契約 (SLA) をご参照ください。

ボリューム ライセンス キーを iText 5 で使用することはできますか?

ボリューム ライセンス キーは iText 5 で使用できますが、自動的に行われる仕組みは使えません。統計情報を記録するためには、カウンター インターフェイスを実装し、それを CounterFactory に登録する必要があります。実装例の詳細は こちら

AGPL プロジェクトでボリューム カウントを使うにはどうしたらいいですか?

AGPL ライセンスをお持ちのお客様は、自分のプロジェクトに最小限のカウンターを使うことができます。これは iText のサーバーを介さず、iText が提供するカウンター インターフェイスのカスタム実装です。以下のコードを呼び出すことで、実装を設定することができます:

Java

      IEventCounterFactory counterFactory = new SimpleEventCounterFactory(new CustomEventCounter());
      EventCounterHandler.getInstance().register(counterFactory);
.NET

      IEventCounterFactory counterFactory = new SimpleEventCounterFactory(new CustomEventCounter());
      EventCounterHandler.GetInstance().Register(counterFactory);

ボリューム カウントの仕組みは?

iText 7 Core

iText では、PdfDocument クラスのインスタンスを "イベント "としてカウントすることで、処理量のカウントを行っています。ドキュメント操作に PdfDocument インスタンスが含まれる場合や、PdfMerger や PdfSplitter などの他のクラスで PdfDocument インスタンスがバックグラウンドで使用される場合に、イベントが記録されます。したがって、PDF の読み取り、書き込み、その他の変更を伴う操作はすべて、ボリューム カウントにカウントされます。
1 つのドキュメントにつき、1 つのイベントしかカウントされません。つまり、PDF を開き、編集し、そしてそれを閉じた場合 1 つのイベントとしてカウントされます。同様に、同じ PdfDocument インスタンスで、ある PDF を読み込み、別の PDF を書くことができることから、PDF を開いて編集し、別のドキュメントとして保存することも 1 つのイベントとしてカウントされます。
PDF を結合したり分割したりすると、複数のイベントが発生します。たとえば、2 つの別々のドキュメントをマージすると、2 つの異なるイベントとしてカウントされます (結合する 2 つのドキュメントそれぞれに別々の PdfDocument インスタンスが存在するため)。分割では、PdfDocument インスタンスを呼び出す PdfSplitter を使用します。したがって、2 ページのドキュメントを分割する場合、1 つのインスタンスでファイルを読み、2 つの分割ファイルをそれぞれ別のインスタンスで書き込むため、3 つのイベントとしてカウントされます。
以下の例では、ボリューム カウンターにイベントがカウントされるさまざまな iText 操作をご紹介します。

例1 - PDF (フォーム) の読み込み

下記の例では、1 イベントとカウントされます。

Java

        final PdfDocument pdfDocument = new PdfDocument(new PdfReader(SRC));
final PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDocument, false);
 
if (acroForm != null) {
    Map fields = acroForm.getFormFields();
    for (Map.Entry entry : fields.entrySet()) {
        System.out.println(entry.getKey() + " is " + entry.getValue().getFormType());
    }
}
 
pdfDocument.close();
C#

        PdfDocument pdfDocument = new PdfDocument(new PdfReader(SRC));
PdfAcroForm acroForm = PdfAcroForm.GetAcroForm(pdfDocument, false);
if (acroForm != null)
{
            foreach (KeyValuePair entry in acroForm.GetFormFields())
            {
                        Console.WriteLine(entry.Key + " is " + entry.Value.GetFormType());
                        pdfDocument.Close();
            }
}
例2 - PDF の書き込み

下記の例でも、1 イベントとカウントされます。

Java

        final PdfWriter pdfWriter = new PdfWriter(DEST);
final PdfDocument pdfDocument = new PdfDocument(pdfWriter);
 
try (final Document document = new Document(pdfDocument)) {
    document.add(new Paragraph("Hello World!"));
}
C#

        PdfWriter pdfWriter = new PdfWriter(DEST);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
using (Document document = new Document(pdfDocument)) {
            document.Add(new Paragraph("Hello World!"));
}
例3 - PDF の分割

分割には、PdfDocument インスタンスを呼び出す PdfSplitter を使用します。したがって、2 ページのドキュメントを分割する場合、1 つのインスタンスでファイルを読み、2 つの分割ファイルをそれぞれ別のインスタンスで書き込むため、3 つのイベントとしてカウントされます。

Java

  private void splitPDF(final String SRC) throws IOException {
    final int maxPageCount = 2; // create a new PDF per 2 pages from the original file
    new PdfSplitter(new PdfDocument(new PdfReader(new File(SRC)))) {
        int partNumber = 1;

        protected PdfWriter getNextPdfWriter(final PageRange documentPageRange) {
            try {
                return new PdfWriter("out/splitDocument_" + partNumber++ + ".pdf");
            } catch (final FileNotFoundException ignored) {
                throw new RuntimeException();
            }
        }
    }.splitByPageCount(maxPageCount, new PdfSplitter.IDocumentReadyListener() {
        @Override
        public void documentReady(final PdfDocument pdfDocument, final PageRange pageRange) {
            pdfDocument.close();
        }
    });
}
C#

  class CustomSplitter : PdfSplitter
        {
            int PartNumber = 1;
            public CustomSplitter(PdfDocument pdfDocument) : base(pdfDocument)
            {
            }
            protected override PdfWriter GetNextPdfWriter(PageRange documentPageRange)
            {
                return new PdfWriter("out/splitDocument_" + PartNumber++ + ".pdf");
            }
            
        }
        private static void Split()
        {
            int MaxPageCount = 2;
            using (var pdfDoc = new PdfDocument(new PdfReader(SRC)))
            {
                var splitter = new CustomSplitter(pdfDoc);
                var splittedDocs = splitter.SplitByPageCount(MaxPageCount);
                foreach (var splittedDoc in splittedDocs)
                {
                    splittedDoc.Close();
                }
            }
        }
例4 - 2 つの PDF の結合

同様に、結合は PdfMerger が PdfDocument を呼び出すことで行われるため、1 インスタンスで 2 つのファイルを読み、その後結合されたファイルを書き込むため、これも 2 イベントとしてカウントされます。

Java

        PdfMerger merge = new PdfMerger(new PdfDocument(new PdfReader(DEST)));
merge.merge(new PdfDocument(new PdfWriter(DEST)), 1, 2);
C#

        PdfMerger merge = new PdfMerger(new PdfDocument(new PdfReader(DEST)));
merge.Merge(new PdfDocument(new PdfWriter(DEST)), 1, 2);

pdfHTML

pdfHTML では、PdfDefaultProcessor#processElements と PdfDefaultProcessor#processDocument の各イベントがカウントされ、ボリューム カウンターでカウントされます。

例5 - HTML から PDF への変換

この HTML 変換は、1x pdfHTML イベントとしてカウントされます。

Java

         
HtmlConverter.convertToPdf(new FileInputStream(ORIG), new FileOutputStream(DEST));
C#

         
HtmlConverter.ConvertToPdf(new FileStream(SRC, FileMode.Open), new FileStream(DEST, FileMode
.Create, FileAccess.Write));

pdfSweep

pdfSweep では、各 PdfCleanUpTool のインスタンスがカウントされます。

例6 - PDF のマスキング

このマスキングの例では、1x Core イベントと 1x pdfSweep イベントの 2 つのイベントがカウントされます。

Java

        try (PdfDocument pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST))) {
          final ICleanupStrategy cleanupStrategy = new RegexBasedCleanupStrategy("Creating").setRedactionColor(ColorConstants.PINK);
          final PdfAutoSweep autoSweep = new PdfAutoSweep(cleanupStrategy);
          autoSweep.cleanUp(pdf);
      }
C#

        using (var pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST)))
        {
                    ICleanupStrategy cleanupStrategy = new RegexBasedCleanupStrategy("Creating")
                                .SetRedactionColor(ColorConstants.PINK);
                    PdfAutoSweep autoSweep = new PdfAutoSweep(cleanupStrategy);
                    autoSweep.CleanUp(pdf);
        }

pdf2Data

例7 - PDF からデータの抽出

pdf2Data では、Pdf2DataExtractor#recognize メソッドの各呼び出しが、1x pdf2Data イベントとしてカウントされます。

Java

        Template template = Pdf2DataExtractor.parseTemplateFromPDF(pathToPdfTemplate);
        Pdf2DataExtractor extractor = new Pdf2DataExtractor(template);
        ParsingResult result = extractor.recognize(pathToFileToParse);
C#

        Template template = Pdf2DataExtractor.ParseTemplateFromPDF(pathToPdfTemplate);
        Pdf2DataExtractor extractor = new Pdf2DataExtractor(template);
        ParsingResult result = extractor.Recognize(pathToFileToParse);

現在の使用量を確認するには?

iText 7.2.x の場合

JSON ライセンス キー ファイルをお持ちで、iText Core 7.2.x またはそのアドオンをお使いの場合、現在の使用量を簡単に確認することができます:

Java

      LicenseKey.loadLicenseFile(new File("license.json"));

for (LicenseInfo licenseInfo : LicenseKey.getLoadedLicensesInfo()) {
    Map map = licenseInfo.getLimitsInfo().getEventsLimitInfo();
    for (Map.Entry entry : map.entrySet()) {
        logger.info("Remaining volume: " + entry.getValue().getRemainingVolume() + " out of " + entry.getValue().getTotalVolume());
    }
}
C#

      LicenseKey.loadLicenseFile(new File("license.json"));

for (LicenseInfo licenseInfo : LicenseKey.getLoadedLicensesInfo()) {
    Map map = licenseInfo.getLimitsInfo().getEventsLimitInfo();
    for (Map.Entry entry : map.entrySet()) {
        logger.info("Remaining volume: " + entry.getValue().getRemainingVolume() + " out of " + entry.getValue().getTotalVolume());
    }
}

iText 7 で PDF の読み書きの回数をカウントするには?

iText 7.1.4 以降の DefaultEventCounter クラスは、iText とそのアドオンが統計情報を送信するために使用します。これは、ファイルの読み書きによらず、イベント システムに基づいているため、iText 7 Core 操作では、Core イベントのみを取得します。これにより、ボリューム カウントの目的が容易になります。
しかし、iText 7 で PDF の読み取りと書き込みの回数をカウントするカスタム ソリューションを実装したい場合、以下の例では、すべての読み取りと書き込みイベントをリストアップしたログファイルを出力するように iText に指示する方法を示しています:


    static class CustomEvent implements IGenericEvent {
      public static final CustomEvent READ = new CustomEvent("read");
      public static final CustomEvent WRITE = new CustomEvent("write");
      private final String subtype;
      private CustomEvent(String subtype) {
                  this.subtype = subtype;
      }

      @Override
      public String getEventType() {
                  return "custom-" + subtype;
      }
      @Override
      public String getOriginId() {
                  return NamespaceConstant.ITEXT;
      }
}

static class ReadingWritingEventsAwarePdfDocument extends PdfDocument {
      public ReadingWritingEventsAwarePdfDocument(PdfReader reader) {
                  super(reader);
      }
      public ReadingWritingEventsAwarePdfDocument(PdfWriter writer) {
                  super(writer);
      }
      public ReadingWritingEventsAwarePdfDocument(PdfReader pdfReader, PdfWriter pdfWriter) {
                  super(pdfReader, pdfWriter);
      }
      // ... any other required constructors matching PdfDocument
      @Override
      protected void open(PdfVersion newPdfVersion) {
                  if (this.reader != null) {
                              EventCounterHandler.getInstance().onEvent(CustomEvent.READ, null, getClass());
                  }
                  if (this.writer != null) {
                              EventCounterHandler.getInstance().onEvent(CustomEvent.WRITE, null, getClass());
                  }
                  super.open(newPdfVersion);
      }
}

public void test() throws IOException {
      // register event-counter factory
      EventCounterHandler.getInstance().register(new SystemOutEventCounterFactory());
      PdfDocument writingDoc = new ReadingWritingEventsAwarePdfDocument(new PdfWriter(DEST_PATH));
      Document document = new Document(writingDoc);
      document.add(new Paragraph("Hello world"));
      document.close();
      System.out.println("--- Document processing separator ---");
      PdfDocument readingDoc = new ReadingWritingEventsAwarePdfDocument(new PdfReader(SOURCE_PATH));
      readingDoc.getPage(1).getPageSize();
      readingDoc.close();
      System.out.println("--- Document processing separator ---");
      PdfDocument stampingDoc = new ReadingWritingEventsAwarePdfDocument(new PdfReader(SOURCE_PATH), new PdfWriter(DEST_PATH));
      new PdfCanvas(stampingDoc.getPage(1)).rectangle(100, 100, 300, 500).fill();
      stampingDoc.close();
}
  

これにより、以下の情報を含むログファイルが作成されます:

[core.clean.CustomCounter$ReadingWritingEventsAwarePdfDocument]custom-write event

[core.clean.CustomCounter$ReadingWritingEventsAwarePdfDocument]core-process event

ボリューム カウントに加えて、カスタム カウンターを使用することはできますか?

はい、ボリューム カウントの実装は、カスタム カウンターと同時に使用することができます。

カスタム ボリューム カウントのコード例を見ることができますか?

iText 7.1.x の場合 Java
public void testCoreEvent() {
      // Use com.itextpdf.kernel.counter.IEventCounterFactory implementation.
      // SimpleEventCounterFactory is the most basic one, simply yielding an instance which is passed to the constructor.
      IEventCounterFactory counterFactory = new SimpleEventCounterFactory(new CustomEventCounter());
      EventCounterHandler.getInstance().register(counterFactory);
      LicenseKey.loadLicenseFile(license);
      PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
      pdfDocument.addNewPage();
      pdfDocument.close();
  
      EventCounterHandler.getInstance().unregister(counterFactory);
  }
  
  class CustomEventCounter extends EventCounter {
  
      @Override
      protected void onEvent(IEvent event, IMetaInfo metaInfo) {
          // This implementation is not thread safe!
          System.out.println("Process event: " + event.getEventType());
      }
  }
C#

    public void testCoreEvent()
    {
        // Use com.itextpdf.kernel.counter.IEventCounterFactory implementation.
        // SimpleEventCounterFactory is the most basic one, simply yielding an instance which is passed to the constructor.
        IEventCounterFactory counterFactory = new SimpleEventCounterFactory(new CustomEventCounter());
        EventCounterHandler.GetInstance().Register(counterFactory);
        LicenseKey.LoadLicenseFile("license path");
        PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new iText.IO.Source.ByteArrayOutputStream()));
        pdfDocument.AddNewPage();
        pdfDocument.Close();

        EventCounterHandler.GetInstance().Unregister(counterFactory);
    }

    class CustomEventCounter : EventCounter
    {
        protected override void OnEvent(IEvent @event, IMetaInfo metaInfo)
        {
            Console.WriteLine("Process Event: ");
        }
    }
  
iText 7.2.x の場合 Java

    public class CountingHandler implements IEventHandler {

      private static Logger logger = LoggerFactory.getLogger(CountingHandler.class);
    
      @Override
      public void onEvent(IEvent iEvent) {
        if (iEvent instanceof ITextCoreProductEvent) {
          ITextCoreProductEvent event = (ITextCoreProductEvent) iEvent;
          logger.info(event.getEventType());
        }
      }
    }
    
    EventManager.getInstance().register(new CountingHandler());
    
  
C#

    class CountingHandler : IEventHandler
{
    public void OnEvent(IEvent @event)
    {
        if (@event is ITextCoreProductEvent)
        {
            Console.WriteLine(((ITextCoreProductEvent)@event).GetEventType());
        }
    }
}

EventManager.GetInstance().Register(new CountingHandler());