def output(params = {})

            has_objstm = self.each_object.any?{|obj| obj.is_a?(ObjectStream)}

            options =
            {
                eol: $/,
                rebuild_xrefs: true,
                noindent: false,
                obfuscate: false,
                use_xrefstm: has_objstm,
                use_xreftable: (not has_objstm),
                up_to_revision: @revisions.size
            }
            options.update(params)

            # Ensures we are using a valid EOL delimiter.
            assert_valid_eol(options[:eol])

            # Do not emit more revisions than present in the document.
            options[:up_to_revision] = [ @revisions.size, options[:up_to_revision] ].min

            # Reset to default params if no xrefs are chosen (hybrid files not supported yet)
            if options[:use_xrefstm] == options[:use_xreftable]
                options[:use_xrefstm] = has_objstm
                options[:use_xreftable] = (not has_objstm)
            end

            # Indent level for objects.
            indent = (options[:noindent] == true ? 0 : 1)

            # Get trailer dictionary
            trailer_dict = self.trailer.dictionary

            prev_xref_offset = nil
            xrefstm_offset = nil

            # Header
            bin = ""
            bin << @header.to_s(eol: options[:eol])

            # For each revision
            @revisions[0, options[:up_to_revision]].each do |rev|

                # Create xref table/stream.
                if options[:rebuild_xrefs] == true
                    lastno_table, lastno_stm = 0, 0
                    brange_table, brange_stm = 0, 0

                    xrefs_stm = [ XRef.new(0, 0, XRef::FREE) ]
                    xrefs_table = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]

                    if options[:use_xreftable] == true
                        xrefsection = XRef::Section.new
                    end

                    if options[:use_xrefstm] == true
                        xrefstm = rev.xrefstm || XRefStream.new
                        if xrefstm == rev.xrefstm
                            xrefstm.clear
                        else
                            add_to_revision(xrefstm, rev)
                        end
                    end
                end

                objset = rev.objects

                objset.find_all{|obj| obj.is_a?(ObjectStream)}.each do |objstm|
                    objset.concat objstm.objects
                end if options[:rebuild_xrefs] == true and options[:use_xrefstm] == true

                previous_obj = nil

                # For each object, in number order
                # Move any XRefStream to the end of the revision.
                objset.sort_by {|obj| [obj.is_a?(XRefStream) ? 1 : 0, obj.no, obj.generation] }
                      .each do |obj|

                    # Ensures that every object has a unique reference number.
                    # Duplicates should never happen in a well-formed revision and will cause breakage of xrefs.
                    if previous_obj and previous_obj.reference == obj.reference
                        raise InvalidPDFError, "Duplicate object detected, reference #{obj.reference}"
                    else
                        previous_obj = obj
                    end

                    # Create xref entry.
                    if options[:rebuild_xrefs] == true

                        # Adding subsections if needed
                        if options[:use_xreftable] and (obj.no - lastno_table).abs > 1
                            xrefsection << XRef::Subsection.new(brange_table, xrefs_table)

                            xrefs_table.clear
                            brange_table = obj.no
                        end

                        if options[:use_xrefstm] and (obj.no - lastno_stm).abs > 1
                            xrefs_stm.each do |xref| xrefstm << xref end
                            xrefstm.Index ||= []
                            xrefstm.Index << brange_stm << xrefs_stm.length

                            xrefs_stm.clear
                            brange_stm = obj.no
                        end

                        # Process embedded objects
                        if options[:use_xrefstm] and obj.parent != obj and obj.parent.is_a?(ObjectStream)
                            index = obj.parent.index(obj.no)

                            xrefs_stm << XRefToCompressedObject.new(obj.parent.no, index)

                            lastno_stm = obj.no
                        else
                            xrefs_stm << XRef.new(bin.size, obj.generation, XRef::USED)
                            xrefs_table << XRef.new(bin.size, obj.generation, XRef::USED)

                            lastno_table = lastno_stm = obj.no
                        end
                    end

                    if obj.parent == obj or not obj.parent.is_a?(ObjectStream)

                        # Finalize XRefStm
                        if options[:rebuild_xrefs] == true and options[:use_xrefstm] == true and obj == xrefstm
                            xrefstm_offset = bin.size

                            xrefs_stm.each do |xref| xrefstm << xref end

                            xrefstm.W = [ 1, (xrefstm_offset.to_s(2).size + 7) >> 3, 2 ]
                            if xrefstm.DecodeParms.is_a?(Dictionary) and xrefstm.DecodeParms.has_key?(:Columns)
                                xrefstm.DecodeParms[:Columns] = xrefstm.W[0] + xrefstm.W[1] + xrefstm.W[2]
                            end

                            xrefstm.Index ||= []
                            xrefstm.Index << brange_stm << xrefs_stm.size

                            xrefstm.dictionary = xrefstm.dictionary.merge(trailer_dict)
                            xrefstm.Prev = prev_xref_offset
                            rev.trailer.dictionary = nil

                            add_to_revision(xrefstm, rev)

                            xrefstm.pre_build
                            xrefstm.post_build
                        end

                        # Output object code
                        if (obj.is_a?(Dictionary) or obj.is_a?(Stream))
                            bin << obj.to_s(eol: options[:eol], indent: indent)
                        else
                            bin << obj.to_s(eol: options[:eol])
                        end
                    end
                end # end each object

                rev.trailer ||= Trailer.new

                # XRef table
                if options[:rebuild_xrefs] == true

                    if options[:use_xreftable] == true
                        table_offset = bin.size

                        xrefsection << XRef::Subsection.new(brange_table, xrefs_table)
                        rev.xreftable = xrefsection

                        rev.trailer.dictionary = trailer_dict
                        rev.trailer.Size = objset.size + 1
                        rev.trailer.Prev = prev_xref_offset

                        rev.trailer.XRefStm = xrefstm_offset if options[:use_xrefstm] == true
                    end

                    startxref = options[:use_xreftable] == true ? table_offset : xrefstm_offset
                    rev.trailer.startxref = prev_xref_offset = startxref

                end

                # Trailer
                bin << rev.xreftable.to_s(eol: options[:eol]) if options[:use_xreftable] == true
                bin << (options[:obfuscate] == true ? rev.trailer.to_obfuscated_str : rev.trailer.to_s(eol: options[:eol], indent: indent))

            end # end each revision

            bin
        end
def output(params = {})

            has_objstm = self.each_object.any?{|obj| obj.is_a?(ObjectStream)}

            options =
            {
                eol: $/,
                rebuild_xrefs: true,
                noindent: false,
                obfuscate: false,
                use_xrefstm: has_objstm,
                use_xreftable: (not has_objstm),
                up_to_revision: @revisions.size
            }
            options.update(params)

            # Ensures we are using a valid EOL delimiter.
            assert_valid_eol(options[:eol])

            # Do not emit more revisions than present in the document.
            options[:up_to_revision] = [ @revisions.size, options[:up_to_revision] ].min

            # Reset to default params if no xrefs are chosen (hybrid files not supported yet)
            if options[:use_xrefstm] == options[:use_xreftable]
                options[:use_xrefstm] = has_objstm
                options[:use_xreftable] = (not has_objstm)
            end

            # Indent level for objects.
            indent = (options[:noindent] == true ? 0 : 1)

            # Get trailer dictionary
            trailer_dict = self.trailer.dictionary

            prev_xref_offset = nil
            xrefstm_offset = nil

            # Header
            bin = ""
            bin << @header.to_s(eol: options[:eol])

            # For each revision
            @revisions[0, options[:up_to_revision]].each do |rev|

                # Create xref table/stream.
                if options[:rebuild_xrefs] == true
                    lastno_table, lastno_stm = 0, 0
                    brange_table, brange_stm = 0, 0

                    xrefs_stm = [ XRef.new(0, 0, XRef::FREE) ]
                    xrefs_table = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]

                    if options[:use_xreftable] == true
                        xrefsection = XRef::Section.new
                    end

                    if options[:use_xrefstm] == true
                        xrefstm = rev.xrefstm || XRefStream.new
                        if xrefstm == rev.xrefstm
                            xrefstm.clear
                        else
                            add_to_revision(xrefstm, rev)
                        end
                    end
                end

                objset = rev.objects

                objset.find_all{|obj| obj.is_a?(ObjectStream)}.each do |objstm|
                    objset.concat objstm.objects
                end if options[:rebuild_xrefs] == true and options[:use_xrefstm] == true

                previous_obj = nil

                # For each object, in number order
                # Move any XRefStream to the end of the revision.
                objset.sort_by {|obj| [obj.is_a?(XRefStream) ? 1 : 0, obj.no, obj.generation] }
                      .each do |obj|

                    # Ensures that every object has a unique reference number.
                    # Duplicates should never happen in a well-formed revision and will cause breakage of xrefs.
                    if previous_obj and previous_obj.reference == obj.reference
                        raise InvalidPDFError, "Duplicate object detected, reference #{obj.reference}"
                    else
                        previous_obj = obj
                    end

                    # Create xref entry.
                    if options[:rebuild_xrefs] == true

                        # Adding subsections if needed
                        if options[:use_xreftable] and (obj.no - lastno_table).abs > 1
                            xrefsection << XRef::Subsection.new(brange_table, xrefs_table)

                            xrefs_table.clear
                            brange_table = obj.no
                        end

                        if options[:use_xrefstm] and (obj.no - lastno_stm).abs > 1
                            xrefs_stm.each do |xref| xrefstm << xref end
                            xrefstm.Index ||= []
                            xrefstm.Index << brange_stm << xrefs_stm.length

                            xrefs_stm.clear
                            brange_stm = obj.no
                        end

                        # Process embedded objects
                        if options[:use_xrefstm] and obj.parent != obj and obj.parent.is_a?(ObjectStream)
                            index = obj.parent.index(obj.no)

                            xrefs_stm << XRefToCompressedObject.new(obj.parent.no, index)

                            lastno_stm = obj.no
                        else
                            xrefs_stm << XRef.new(bin.size, obj.generation, XRef::USED)
                            xrefs_table << XRef.new(bin.size, obj.generation, XRef::USED)

                            lastno_table = lastno_stm = obj.no
                        end
                    end

                    if obj.parent == obj or not obj.parent.is_a?(ObjectStream)

                        # Finalize XRefStm
                        if options[:rebuild_xrefs] == true and options[:use_xrefstm] == true and obj == xrefstm
                            xrefstm_offset = bin.size

                            xrefs_stm.each do |xref| xrefstm << xref end

                            xrefstm.W = [ 1, (xrefstm_offset.to_s(2).size + 7) >> 3, 2 ]
                            if xrefstm.DecodeParms.is_a?(Dictionary) and xrefstm.DecodeParms.has_key?(:Columns)
                                xrefstm.DecodeParms[:Columns] = xrefstm.W[0] + xrefstm.W[1] + xrefstm.W[2]
                            end

                            xrefstm.Index ||= []
                            xrefstm.Index << brange_stm << xrefs_stm.size

                            xrefstm.dictionary = xrefstm.dictionary.merge(trailer_dict)
                            xrefstm.Prev = prev_xref_offset
                            rev.trailer.dictionary = nil

                            add_to_revision(xrefstm, rev)

                            xrefstm.pre_build
                            xrefstm.post_build
                        end

                        # Output object code
                        if (obj.is_a?(Dictionary) or obj.is_a?(Stream))
                            bin << obj.to_s(eol: options[:eol], indent: indent)
                        else
                            bin << obj.to_s(eol: options[:eol])
                        end
                    end
                end # end each object

                rev.trailer ||= Trailer.new

                # XRef table
                if options[:rebuild_xrefs] == true

                    if options[:use_xreftable] == true
                        table_offset = bin.size

                        xrefsection << XRef::Subsection.new(brange_table, xrefs_table)
                        rev.xreftable = xrefsection

                        rev.trailer.dictionary = trailer_dict
                        rev.trailer.Size = objset.size + 1
                        rev.trailer.Prev = prev_xref_offset

                        rev.trailer.XRefStm = xrefstm_offset if options[:use_xrefstm] == true
                    end

                    startxref = options[:use_xreftable] == true ? table_offset : xrefstm_offset
                    rev.trailer.startxref = prev_xref_offset = startxref

                end

                # Trailer
                bin << rev.xreftable.to_s(eol: options[:eol]) if options[:use_xreftable] == true
                bin << (options[:obfuscate] == true ? rev.trailer.to_obfuscated_str : rev.trailer.to_s(eol: options[:eol], indent: indent))

            end # end each revision

            bin
        end
Returns the final binary representation of the current document.