ジャーナリング#

バージョン1.19.0から、PDFドキュメントの更新時にジャーナリングが可能になりました。

ジャーナリングは、PDFへの変更を元に戻したり再適用したりするためのログ記録メカニズムです。近代的なデータベースシステムにおける「論理ユニット・オブ・ワーク(LUW)」のように、一連の更新を「操作」としてグループ化することができます。MuPDFジャーナリングでは、操作がLUWの役割を果たします。

注釈

データベースシステムで見られるLUWの実装とは異なり、MuPDFジャーナリングはドキュメント単位で行われます。複数のPDFで同時に更新するためのサポートはありません。それぞれのPDFに対して独自のロジックを確立する必要があります。

  • ジャーナリングは、ドキュメントメソッドを介して有効にする必要があります。既存のドキュメントまたは新規ドキュメントのジャーナリングが可能です。ファイルを閉じることでのみジャーナリングを無効にすることができます。

  • 有効になると、すべての変更は操作の内部で行われる必要があります。そうでない場合、例外が発生します。操作はドキュメントメソッドを介して開始および停止されます。これらの呼び出し間で行われる更新はLUWを形成し、集合的に元に戻すか再適用するために使用できます。MuPDFの用語で言えば、「元に戻す」または「やり直す」ことができます。

  • いつでも、ジャーナリングの状態をクエリできます。ジャーナリングがアクティブかどうか、いくつの操作が記録されたか、元に戻すかやり直すかが可能か、ジャーナル内の現在位置などがわかります。

  • ジャーナルはファイルに保存またはロードできます。これらはドキュメントメソッドです。

  • ジャーナルファイルをロードする際には、ドキュメントとの互換性がチェックされ、成功した場合に自動的にジャーナリングが有効になります。

  • ジャーナリングされている既存のPDFに対しては、特別な新しい保存メソッドが利用可能です。Document.save_snapshot() これにより、これまでにジャーナリングされたすべての更新を含む特別なインクリメンタル保存が行われます。そのジャーナルも同時に保存される場合(ドキュメントスナップショットの直後に保存)、ドキュメントとジャーナルは同期され、後で操作を元に戻したりやり直したりするために一緒に使用できるようになります。まるで中断がなかったかのように。

  • スナップショットPDFは、あらゆる側面で有効なPDFであり、完全に使用可能です。ただし、ジャーナルファイルを使用せずにドキュメントが変更された場合、同期が取れなくなり、ジャーナルは利用できなくなります。

  • スナップショットファイルはインクリメンタルな更新のように構造化されています。ただし、内部のジャーナリングロジックでは、新しいファイルに保存する必要があります。したがって、ユーザーはオリジナルのPDF(例: original.pdf )とそのスナップショットセット(例: original-snap1.pdf / original-snap1.logoriginal-snap2.pdf / original-snap2.log など)の間に認識可能な関係をサポートするためのファイル命名規則を開発する必要があります。

例セッション1#

説明:

  • 新しいPDFを作成し、ジャーナリングを有効にします。次に、ページを追加し、いくつかのテキスト行を別々の操作として追加します。

  • ジャーナル内を移動し、これらの更新を元に戻したりやり直したりし、状態やファイルの結果を表示します:

    >>> import pymupdf
    >>> doc=pymupdf.open()
    >>> doc.journal_enable()
    
    >>> # try update without an operation:
    >>> page = doc.new_page()
    mupdf: No journalling operation started
    ... omitted lines
    RuntimeError: No journalling operation started
    
    >>> doc.journal_start_op("op1")
    >>> page = doc.new_page()
    >>> doc.journal_stop_op()
    
    >>> doc.journal_start_op("op2")
    >>> page.insert_text((100,100), "Line 1")
    >>> doc.journal_stop_op()
    
    >>> doc.journal_start_op("op3")
    >>> page.insert_text((100,120), "Line 2")
    >>> doc.journal_stop_op()
    
    >>> doc.journal_start_op("op4")
    >>> page.insert_text((100,140), "Line 3")
    >>> doc.journal_stop_op()
    
    >>> # show position in journal
    >>> doc.journal_position()
    (4, 4)
    >>> # 4 operations recorded - positioned at bottom
    >>> # what can we do?
    >>> doc.journal_can_do()
    {'undo': True, 'redo': False}
    >>> # currently only undos are possible. Print page content:
    >>> print(page.get_text())
    Line 1
    Line 2
    Line 3
    
    >>> # undo last insert:
    >>> doc.journal_undo()
    >>> # show combined status again:
    >>> doc.journal_position();doc.journal_can_do()
    (3, 4)
    {'undo': True, 'redo': True}
    >>> print(page.get_text())
    Line 1
    Line 2
    
    >>> # our position is now second to last
    >>> # last text insertion was reverted
    >>> # but we can redo / move forward as well:
    >>> doc.journal_redo()
    >>> # our combined status:
    >>> doc.journal_position();doc.journal_can_do()
    (4, 4)
    {'undo': True, 'redo': False}
    >>> print(page.get_text())
    Line 1
    Line 2
    Line 3
    >>> # line 3 has appeared again!
    

例セッション2#

説明:

  • 前回と同様ですが、いくつかの操作を元に戻した後、異なる更新を追加します。これにより、次のことが起こります:

    • 元に戻されたジャーナルエントリが永久に削除されます。

    • 新しい更新操作は新しい最後のエントリになります。

    >>> doc=pymupdf.open()
    >>> doc.journal_enable()
    >>> doc.journal_start_op("Page insert")
    >>> page=doc.new_page()
    >>> doc.journal_stop_op()
    >>> for i in range(5):
            doc.journal_start_op("insert-%i" % i)
            page.insert_text((100, 100 + 20*i), "text line %i" %i)
            doc.journal_stop_op()
    
    >>> # combined status info:
    >>> doc.journal_position();doc.journal_can_do()
    (6, 6)
    {'undo': True, 'redo': False}
    
    >>> for i in range(3):  # revert last three operations
            doc.journal_undo()
    >>> doc.journal_position();doc.journal_can_do()
    (3, 6)
    {'undo': True, 'redo': True}
    
    >>> # now do a different update:
    >>> doc.journal_start_op("Draw some line")
    >>> page.draw_line((100,150), (300,150))
    Point(300.0, 150.0)
    >>> doc.journal_stop_op()
    >>> doc.journal_position();doc.journal_can_do()
    (4, 4)
    {'undo': True, 'redo': False}
    
    >>> # this has changed the journal:
    >>> # previous last 3 text line operations were removed, and
    >>> # we have only 4 operations: drawing the line is the new last one
    

This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of that license. Refer to licensing information at artifex.com or contact Artifex Software Inc., 39 Mesa Street, Suite 108A, San Francisco CA 94129, United States for further information.

Discord logo