File: snapshot.rb

package info (click to toggle)
ruby-prawn 1.0.0~rc1%2Bdfsg1-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 4,248 kB
  • sloc: ruby: 17,499; sh: 44; makefile: 17
file content (89 lines) | stat: -rw-r--r-- 3,025 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# encoding: utf-8

# snapshot.rb : Implements transactional rendering for Prawn
#
# Copyright August 2009, Brad Ediger.  All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.

require 'delegate'

module Prawn
  class Document
    module Snapshot

      RollbackTransaction = Class.new(StandardError)

      # Call this within a +transaction+ block to roll back the transaction and
      # prevent any of its data from being rendered. You must reset the
      # y-position yourself if you have performed any drawing operations that
      # modify it.
      #
      def rollback
        raise RollbackTransaction
      end

      # Run a block of drawing operations, to be completed atomically. If
      # +rollback+ is called or a RollbackTransaction exception is raised
      # inside the block, all actions taken inside the block will be rolled
      # back (with the exception of y-position, which you must restore
      # yourself). 
      #
      # Returns true on success, or false if the transaction was rolled back.
      #
      def transaction
        snap = take_snapshot
        yield
        true
      rescue RollbackTransaction
        restore_snapshot(snap)
        false
      end

      private
      
      # Takes a current snapshot of the document's state, sufficient to
      # reconstruct it after it was amended.
      #
      def take_snapshot
        # current_page holds a ref to the Pages dictionary which grows
        # monotonically as data is added to the document, so we share that
        # between the old and new copies.
        {:page_content    => state.page.content.deep_copy,
         :current_page    => state.page.dictionary.deep_copy(share=[:Parent]),
         :bounds          => bounds.deep_copy,
         :page_number     => page_number,
         :page_kids       => state.store.pages.data[:Kids].map{|kid| kid.identifier},
         :dests           => names? && names.data[:Dests].deep_copy}
      end

      # Rolls the page state back to the state of the given snapshot.
      #
      def restore_snapshot(shot)
        page = state.page
        # Because these objects are referenced by identifier from the Pages
        # dictionary, we can't just restore them over the current refs in
        # page_content and current_page. We have to restore them over the old
        # ones.
        page.content = shot[:page_content].identifier
        page.content.replace shot[:page_content]

        page.dictionary = shot[:current_page].identifier
        page.dictionary.replace shot[:current_page]
        page.dictionary.data[:Contents] = page.content

        self.page_number = shot[:page_number]

        state.store.pages.data[:Kids] = shot[:page_kids].map{|id| state.store[id]}
        state.store.pages.data[:Count] = shot[:page_kids].size

        self.bounds = BoundingBox.restore_deep_copy(shot[:bounds], self)

        if shot[:dests]
          names.data[:Dests] = shot[:dests] 
        end
      end

    end
  end
end