def generate_view_page(path, macro_id, host_url, save_values={}):
    '''
    Generate a macro view page. Returns None on error.
    Propogates exceptions up.
    '''
    ret_page = None

    # Make sure we got valid input.
    if not macro_id: raise NoInputError("You entered an invalid macro ID.  Try again?")

    # Ensure we have a saved macro.  Will throw exception on fail.
    saved_macro = SavedMacroOps(macro_id)

    # View pages are extremely heavy, and the majority of their
    # data doesn't change.  Use helper to handle this.
    macro_form_template_values = get_view_dict_from_macro_id(macro_id, saved_macro)

    # Add in edit and view links.
    # Add in the author link.
    # Add in the form ids.
    page_values = {'macro_link'      : '%s/%s'    % (host_url,
                                                    macro_id),
                   'macro_edit'      : '%s?%s=%s' % (host_url,
                                                     GET_MACRO_LINK,
                                                     macro_id),
                   'author'          : _format_author(saved_macro.entity.name,
                                                      saved_macro.entity.server),
                   'send_email'      : FORM_MACRO_EMAIL,
                   'send_input_form' : FORM_EMAIL_INPUT,
                   'to'              : FORM_EMAIL_TO,
                   'from'            : FORM_EMAIL_FROM,
                   }
    macro_form_template_values.update(page_values)
    
    # Generate the dynamic parts of the page:
    #   1. Increment the view counter on this macro.
    #   2. Get the rating in terms of stars for this macro.
    dynamic_page_vals = {
        'stars'           : [i + 1 for i in range(MAX_RATING)],
        'num_rates'       : saved_macro.entity.num_rates,
        'views'           : saved_macro.add_to_view_count(),
        'rating'          : saved_macro.get_rating_dict(saved_macro.get_rating()),
        }
    macro_form_template_values.update(dynamic_page_vals)
    
    # Is this another save attempt after errors?  If so, update
    # with errors and previous values.
    macro_form_template_values.update(save_values)

    # Call helper to interpret the macro behind the id.
    macro_obj = get_macro_obj_from_id(macro_id, saved_macro.entity.macro)

    # Populate the templace with the processed macro.
    macro_form_template_values['processed_macro_html'] = \
         render_macro(macro_obj, path)

    # Get the copy and paste form of the macro
    macro_form_template_values['copy_paste'] = \
        _render_copy_cmd(macro_obj)

    # Render the macro view template html.
    ret_page  = render_template('view.template',
                                macro_form_template_values,
                                path)
    return render_template('base.template',
                           {'content':  ret_page},
                           path)