diff mupdf-source/scripts/wrap/classes.py @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/scripts/wrap/classes.py	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,2048 @@
+'''
+Extra information to customise how we wrap MuPDF structs into C++ classes.
+'''
+
+import textwrap
+
+import jlib
+
+from . import rename
+from . import state
+from . import util
+
+
+class ExtraMethod:
+    '''
+    Defines a prototype and implementation of a custom method in a generated
+    class.
+    '''
+    def __init__( self, return_, name_args, body, comment, overload=None):
+        '''
+        return_:
+            Return type as a string.
+        name_args:
+            A string describing name and args of the method:
+                <method-name>(<args>)
+        body:
+            Implementation code including the enclosing '{...}'.
+        comment:
+            Optional comment; should include /* and */ or //.
+        overload:
+            If true, we allow auto-generation of methods with same name.
+        '''
+        assert name_args
+        self.return_ = return_
+        self.name_args = name_args
+        self.body = body
+        self.comment = comment
+        self.overload = overload
+        assert '\t' not in body
+    def __str__(self):
+        return f'{self.name_args} => {self.return_}'
+
+
+class ExtraConstructor:
+    '''
+    Defines a prototype and implementation of a custom constructor in a
+    generated class.
+    '''
+    def __init__( self, name_args, body, comment):
+        '''
+        name_args:
+            A string of the form: (<args>)
+        body:
+            Implementation code including the enclosing '{...}'.
+        comment:
+            Optional comment; should include /* and */ or //.
+        '''
+        self.return_ = ''
+        self.name_args = name_args
+        self.body = body
+        self.comment = comment
+        assert '\t' not in body
+
+
+class ClassExtra:
+    '''
+    Extra methods/features when wrapping a particular MuPDF struct into a C++
+    class.
+    '''
+    def __init__( self,
+            accessors=None,
+            class_bottom='',
+            class_post='',
+            class_pre='',
+            class_top='',
+            constructor_default=True,
+            constructor_excludes=None,
+            constructor_prefixes=None,
+            constructor_raw=True,
+            constructors_extra=None,
+            constructors_wrappers=None,
+            copyable=True,
+            extra_cpp='',
+            iterator_next=None,
+            methods_extra=None,
+            method_wrappers=None,
+            method_wrappers_static=None,
+            opaque=False,
+            pod=False,
+            virtual_fnptrs=False,
+            ):
+        '''
+        accessors:
+            If true, we generate accessors methods for all items in the
+            underlying struct.
+
+            Defaults to True if pod is True, else False.
+
+        class_bottom:
+            Extra text at end of class definition, e.g. for member variables.
+
+        class_post:
+            Extra text after class definition, e.g. complete definition of
+            iterator class.
+
+        class_pre:
+            Extra text before class definition, e.g. forward declaration of
+            iterator class.
+
+        class_top:
+            Extra text at start of class definition, e.g. for enums.
+
+        constructor_default:
+            If None we set to true if `pod` is true, otherwise false. If
+            true, we create a default constructor. If `pod` is true this
+            constructor will default-initialise each member, otherwise it will
+            set `m_internal` to null.
+
+        constructor_excludes:
+            Lists of constructor functions to ignore.
+
+        constructor_prefixes:
+            Extra fz_*() function name prefixes that can be used by class
+            constructors_wrappers. We find all functions whose name starts with one of
+            the specified prefixes and which returns a pointer to the relevant
+            fz struct.
+
+            For each function we find, we make a constructor that takes the
+            required arguments and set m_internal to what this function
+            returns.
+
+            If there is a '-' item, we omit the default 'fz_new_<type>' prefix.
+
+        constructor_raw:
+            If true, create a constructor that takes a pointer to an instance
+            of the wrapped fz_ struct. If 'default', this constructor arg
+            defaults to NULL. If 'declaration_only' we declare the constructor
+            but do not write out the function definition - typically this will
+            be instead specified as custom code in <extra_cpp>.
+
+        constructors_extra:
+            List of ExtraConstructor's, allowing arbitrary constructors_wrappers to be
+            specified.
+
+        constructors_wrappers:
+            List of fns to use as constructors_wrappers.
+
+        copyable:
+            If 'default' we allow default copy constructor to be created by C++
+            compiler. This is useful for plain structs that are not referenced
+            counted but can still be copied, but which we don't want to specify
+            pod=True.
+
+            Otherwise if true, generated wrapper class must be copyable. If
+            pod is false, we generate a copy constructor by looking for a
+            fz_keep_*() function; it's an error if we can't find this function
+            [2024-01-24 fixme: actually we don't appear to raise an
+            error in this case, instead we make class non-copyable.
+            e.g. FzCompressedBuffer.].
+
+            Otherwise if false we create a private copy constructor.
+
+            [todo: need to check docs for interaction of pod/copyable.]
+
+        extra_cpp:
+            Extra text for .cpp file, e.g. implementation of iterator class
+            methods.
+
+        iterator_next:
+            Support for iterating forwards over linked list.
+
+            Should be (first, last).
+
+            first:
+                Name of element within the wrapped class that points to the
+                first item in the linked list. We assume that this element will
+                have 'next' pointers that terminate in NULL.
+
+                If <first> is '', the container is itself the first element in
+                the linked list.
+
+            last:
+                Currently unused, but could be used for reverse iteration in
+                the future.
+
+            We generate begin() and end() methods, plus a separate iterator
+            class, to allow iteration over the linked list starting at
+            <structname>::<first> and iterating to ->next until we reach NULL.
+
+        methods_extra:
+            List of ExtraMethod's, allowing arbitrary methods to be specified.
+
+        method_wrappers:
+            Extra fz_*() function names that should be wrapped in class
+            methods.
+
+            E.g. 'fz_foo_bar' is converted to a method called foo_bar()
+            that takes same parameters as fz_foo_bar() except context and
+            any pointer to struct and fz_context*. The implementation calls
+            fz_foo_bar(), converting exceptions etc.
+
+            The first arg that takes underlying fz_*_s type is omitted and
+            implementation passes <this>.
+
+        method_wrappers_static:
+            Like <method_wrappers>, but generates static methods, where no args
+            are replaced by <this>.
+
+        opaque:
+            If true, we generate a wrapper even if there's no definition
+            available for the struct, i.e. it's only available as a forward
+            declaration.
+
+        pod:
+            If 'inline', there is no m_internal; instead, each member of the
+            underlying class is placed in the wrapper class and special method
+            `internal()` returns a fake pointer to underlying class.
+
+            If 'none', there is no m_internal member at all. Typically
+            <extra_cpp> could be used to add in custom members.
+
+            If True, underlying class is POD and m_internal is an instance of
+            the underlying class instead of a pointer to it.
+
+        virtual_fnptrs:
+            If true, should be a dict with these keys:
+
+                alloc:
+                    A string containing C++ code to be embedded in the
+                    virtual_fnptrs wrapper class's constructor.
+
+                    If the wrapper class is a POD, the MuPDF struct is already
+                    available as part of the wrapper class instance (as
+                    m_internal, or `internal()` if inline). Otherwise this
+                    code should set `m_internal` to point to a newly allocated
+                    instance of the MuPDF struct.
+
+                    Should typically set up the MuPDF struct so that `self_()`
+                    can return the original C++ wrapper class instance.
+                comment:
+                    Extra comment for the wrapper class.
+                free:
+                    Optional code for freeing the virtual_fnptrs wrapper
+                    class. If specified this causes creation of a destructor
+                    function.
+
+                    This is only needed for non-ref-counted classes that are
+                    not marked as POD. In this case the wrapper class has a
+                    pointer `m_internal` member that will not be automatically
+                    freed by the destructor, and `alloc` will usually have set
+                    it to a newly allocated struct.
+                self_:
+                    A callable that returns a string containing C++ code for
+                    embedding in each low-level callback. It should returns a
+                    pointer to the original C++ virtual_fnptrs wrapper class.
+
+                    If `self_n` is None, this callable takes no args. Otherwise
+                    it takes the name of the `self_n`'th arg.
+                self_n:
+                    Index of arg in each low-level callback, for the arg that
+                    should be passed to `self_`. We use the same index for all
+                    low-level callbacks.  If not specified, default is 1 (we
+                    generally expect args to be (fz_context* ctx, void*, ...).
+                    If None, `self_` is called with no arg; this is for if we
+                    use a different mechanism such as a global variable.
+
+            We generate a virtual_fnptrs wrapper class, derived from the main
+            wrapper class, where the main wrapper class's function pointers end
+            up calling the virtual_fnptrs wrapper class's virtual methods. We
+            then use SWIG's 'Director' support to allow these virtual methods
+            to be overridden in Python/C#. Thus one can make MuPDF function
+            pointers call Python/C# code.
+        '''
+        if accessors is None and pod is True:
+            accessors = True
+        if constructor_default is None:
+            constructor_default = pod
+        self.accessors = accessors
+        self.class_bottom = class_bottom
+        self.class_post = class_post
+        self.class_pre = class_pre
+        self.class_top = class_top
+        self.constructor_default = constructor_default
+        self.constructor_excludes = constructor_excludes or []
+        self.constructor_prefixes = constructor_prefixes or []
+        self.constructor_raw = constructor_raw
+        self.constructors_extra = constructors_extra or []
+        self.constructors_wrappers = constructors_wrappers or []
+        self.copyable = copyable
+        self.extra_cpp = extra_cpp
+        self.iterator_next = iterator_next
+        self.methods_extra = methods_extra or []
+        self.method_wrappers = method_wrappers or []
+        self.method_wrappers_static = method_wrappers_static or []
+        self.opaque = opaque
+        self.pod = pod
+        self.virtual_fnptrs = virtual_fnptrs
+
+        assert self.pod in (False, True, 'inline', 'none'), f'{self.pod}'
+
+        def assert_list_of( items, type_):
+            assert isinstance( items, list)
+            for item in items:
+                assert isinstance( item, type_)
+
+        assert_list_of( self.constructor_prefixes, str)
+        assert_list_of( self.method_wrappers, str)
+        assert_list_of( self.method_wrappers_static, str)
+        assert_list_of( self.methods_extra, ExtraMethod)
+        assert_list_of( self.constructors_extra, ExtraConstructor)
+
+        if virtual_fnptrs:
+            assert isinstance(virtual_fnptrs, dict), f'virtual_fnptrs={virtual_fnptrs!r}'
+
+    def __str__( self):
+        ret = ''
+        ret += f' accessors={self.accessors}'
+        ret += f' class_bottom={self.class_bottom}'
+        ret += f' class_post={self.class_post}'
+        ret += f' class_pre={self.class_pre}'
+        ret += f' class_top={self.class_top}'
+        ret += f' constructor_default={self.constructor_default}'
+        ret += f' constructor_excludes={self.constructor_excludes}'
+        ret += f' constructor_prefixes={self.constructor_prefixes}'
+        ret += f' constructor_raw={self.constructor_raw}'
+        ret += f' constructors_extra={self.constructors_extra}'
+        ret += f' constructors_wrappers={self.constructors_wrappers}'
+        ret += f' copyable={self.copyable}'
+        ret += f' extra_cpp={self.extra_cpp}'
+        ret += f' iterator_next={self.iterator_next}'
+        ret += f' methods_extra={self.methods_extra}'
+        ret += f' method_wrappers={self.method_wrappers}'
+        ret += f' method_wrappers_static={self.method_wrappers_static}'
+        ret += f' opaque={self.opaque}'
+        ret += f' pod={self.pod}'
+        ret += f' virtual_fnptrs={self.virtual_fnptrs}'
+        return ret
+
+
+
+class ClassExtras:
+    '''
+    Extra methods/features for each of our auto-generated C++ wrapper classes.
+    '''
+    def __init__( self, **namevalues):
+        '''
+        namevalues:
+            Named args mapping from struct name (e.g. fz_document) to a
+            ClassExtra.
+        '''
+        self.items = dict()
+        for name, value in namevalues.items():
+            self.items[ name] = value
+
+    def get( self, tu, name):
+        '''
+        Searches for <name> and returns a ClassExtra instance. If <name> is not
+        found, we insert an empty ClassExtra instance and return it. We do this
+        for any name, e.g. name could be 'foo *'.
+
+        We return None if <name> is a known enum.
+        '''
+        verbose = state.state_.show_details( name)
+        if 0 and verbose:
+            jlib.log( 'ClassExtras.get(): {=name}')
+        name = util.clip( name, ('const ', 'struct '))
+        if 0 and verbose:
+            jlib.log( 'ClassExtras.get(): {=name}')
+        if not name.startswith( ('fz_', 'pdf_')):
+            return
+
+        ret = self.items.setdefault( name, ClassExtra())
+
+        if name in state.state_.enums[ tu]:
+            #jlib.log( '*** name is an enum: {name=}')
+            return None
+
+        if ' ' not in name and not ret.pod and ret.copyable and ret.copyable != 'default':
+            # Check whether there is a _keep() fn.
+            keep_name = f'fz_keep_{name[3:]}' if name.startswith( 'fz_') else f'pdf_keep_{name[4:]}'
+            keep_cursor = state.state_.find_function( tu, keep_name, method=True)
+            if not keep_cursor:
+                if ret.copyable:
+                    if 0:
+                        jlib.log( '*** Changing .copyable to False for {=name keep_name}')
+                ret.copyable = False
+        return ret
+
+    def get_or_none( self, name):
+        return self.items.get( name)
+
+
+# Customisation information for selected wrapper classes.
+#
+# We use MuPDF struct names as keys.
+#
+classextras = ClassExtras(
+
+        fz_aa_context = ClassExtra(
+                pod='inline',
+                ),
+
+        fz_band_writer = ClassExtra(
+                class_top = '''
+                    /* We use these enums to support construction via all the relevant MuPDF functions. */
+
+                    enum Cm
+                    {
+                        MONO,
+                        COLOR,
+                    };
+                    enum P
+                    {
+                        PNG,
+                        PNM,
+                        PAM,
+                        PBM,
+                        PKM,
+                        PS,
+                        PSD,
+                    };
+                    ''',
+                constructors_extra = [
+                    ExtraConstructor(
+                        f'({rename.class_("fz_output")}& out, Cm cm, const {rename.class_("fz_pcl_options")}& options)',
+                        f'''
+                        {{
+                            ::fz_output*            out2 = out.m_internal;
+                            const ::fz_pcl_options* options2 = options.m_internal;
+                            if (0)  {{}}
+                            else if (cm == MONO)    m_internal = {rename.ll_fn('fz_new_mono_pcl_band_writer' )}( out2, options2);
+                            else if (cm == COLOR)   m_internal = {rename.ll_fn('fz_new_color_pcl_band_writer')}( out2, options2);
+                            else throw std::runtime_error( "Unrecognised fz_band_writer_s Cm type");
+                        }}
+                        ''',
+                        comment = f'/* Constructor using fz_new_mono_pcl_band_writer() or fz_new_color_pcl_band_writer(). */',
+                        ),
+                    ExtraConstructor(
+                        f'({rename.class_("fz_output")}& out, P p)',
+                        f'''
+                        {{
+                            ::fz_output*    out2 = out.m_internal;
+                            if (0)  {{}}
+                            else if (p == PNG)  m_internal = {rename.ll_fn('fz_new_png_band_writer')}( out2);
+                            else if (p == PNM)  m_internal = {rename.ll_fn('fz_new_pnm_band_writer')}( out2);
+                            else if (p == PAM)  m_internal = {rename.ll_fn('fz_new_pam_band_writer')}( out2);
+                            else if (p == PBM)  m_internal = {rename.ll_fn('fz_new_pbm_band_writer')}( out2);
+                            else if (p == PKM)  m_internal = {rename.ll_fn('fz_new_pkm_band_writer')}( out2);
+                            else if (p == PS)   m_internal = {rename.ll_fn('fz_new_ps_band_writer' )}( out2);
+                            else if (p == PSD)  m_internal = {rename.ll_fn('fz_new_psd_band_writer')}( out2);
+                            else throw std::runtime_error( "Unrecognised fz_band_writer_s P type");
+                        }}
+                        ''',
+                        comment = f'/* Constructor using fz_new_p*_band_writer(). */',
+                        ),
+                    ExtraConstructor(
+                        f'({rename.class_("fz_output")}& out, Cm cm, const {rename.class_("fz_pwg_options")}& options)',
+                        f'''
+                        {{
+                            ::fz_output*            out2 = out.m_internal;
+                            const ::fz_pwg_options* options2 = &options.m_internal;
+                            if (0)  {{}}
+                            else if (cm == MONO)    m_internal = {rename.ll_fn('fz_new_mono_pwg_band_writer' )}( out2, options2);
+                            else if (cm == COLOR)   m_internal = {rename.ll_fn('fz_new_pwg_band_writer')}( out2, options2);
+                            else throw std::runtime_error( "Unrecognised fz_band_writer_s Cm type");
+                        }}
+                        ''',
+                        comment = f'/* Constructor using fz_new_mono_pwg_band_writer() or fz_new_pwg_band_writer(). */',
+                        ),
+                    ],
+                copyable = False,
+                ),
+
+        fz_bitmap = ClassExtra(
+                accessors = True,
+                ),
+
+        fz_buffer = ClassExtra(
+                constructor_raw = 'default',
+                constructors_wrappers = [
+                    'fz_read_file',
+                    ],
+                ),
+
+        fz_color_params = ClassExtra(
+                pod='inline',
+                constructors_extra = [
+                    ExtraConstructor('()',
+                        f'''
+                        {{
+                            this->ri = fz_default_color_params.ri;
+                            this->bp = fz_default_color_params.bp;
+                            this->op = fz_default_color_params.op;
+                            this->opm = fz_default_color_params.opm;
+                        }}
+                        ''',
+                        comment = '/* Equivalent to fz_default_color_params. */',
+                        ),
+                    ],
+                ),
+
+        fz_colorspace = ClassExtra(
+                constructors_extra = [
+                    ExtraConstructor(
+                        '(Fixed fixed)',
+                        f'''
+                        {{
+                            if (0) {{}}
+                            else if ( fixed == Fixed_GRAY)  m_internal = {rename.ll_fn( 'fz_device_gray')}();
+                            else if ( fixed == Fixed_RGB)   m_internal = {rename.ll_fn( 'fz_device_rgb' )}();
+                            else if ( fixed == Fixed_BGR)   m_internal = {rename.ll_fn( 'fz_device_bgr' )}();
+                            else if ( fixed == Fixed_CMYK)  m_internal = {rename.ll_fn( 'fz_device_cmyk')}();
+                            else if ( fixed == Fixed_LAB)   m_internal = {rename.ll_fn( 'fz_device_lab' )}();
+                            else {{
+                                std::string message = "Unrecognised fixed colorspace id";
+                                throw {rename.error_class("FZ_ERROR_GENERIC")}(message.c_str());
+                            }}
+                            {rename.ll_fn('fz_keep_colorspace')}(m_internal);
+                        }}
+                        ''',
+                        comment = '/* Construct using one of: fz_device_gray(), fz_device_rgb(), fz_device_bgr(), fz_device_cmyk(), fz_device_lab(). */',
+                        ),
+                    ],
+                constructor_raw=1,
+                class_top = '''
+                        /* We use this enums to support construction via all the relevant MuPDF functions. */
+                        enum Fixed
+                        {
+                            Fixed_GRAY,
+                            Fixed_RGB,
+                            Fixed_BGR,
+                            Fixed_CMYK,
+                            Fixed_LAB,
+                        };
+                        ''',
+                ),
+
+        fz_compressed_buffer = ClassExtra(
+                methods_extra = [
+                    ExtraMethod(
+                        rename.class_('fz_buffer'),
+                        'get_buffer()',
+                        textwrap.dedent(f'''
+                            {{
+                                return {rename.class_('fz_buffer')}(
+                                        {rename.ll_fn('fz_keep_buffer')}(m_internal->buffer)
+                                        );
+                            }}
+                            '''),
+                        '/* Returns wrapper class for fz_buffer *m_internal.buffer. */',
+                        ),
+                    ],
+                ),
+
+        fz_context = ClassExtra(
+                copyable = False,
+                ),
+
+        fz_cookie = ClassExtra(
+                constructors_extra = [
+                    ExtraConstructor( '()',
+                    '''
+                    {
+                        this->m_internal.abort = 0;
+                        this->m_internal.progress = 0;
+                        this->m_internal.progress_max = (size_t) -1;
+                        this->m_internal.errors = 0;
+                        this->m_internal.incomplete = 0;
+                    }
+                    ''',
+                    comment = '/* Default constructor sets all fields to default values. */',
+                    ),
+                    ],
+                constructor_raw = False,
+                methods_extra = [
+                    ExtraMethod(
+                            'void',
+                            'set_abort()',
+                            '{ m_internal.abort = 1; }\n',
+                            '/* Sets m_internal.abort to 1. */',
+                            ),
+                    ExtraMethod(
+                            'void',
+                            'increment_errors(int delta)',
+                            '{ m_internal.errors += delta; }\n',
+                            '/* Increments m_internal.errors by <delta>. */',
+                            ),
+                ],
+                pod = True,
+                # Other code asynchronously writes to our fields, so we are not
+                # copyable. todo: maybe tie us to all objects to which we have
+                # been associated?
+                #
+                copyable=False,
+                ),
+
+        fz_device = ClassExtra(
+                virtual_fnptrs = dict(
+                    self_ = lambda name: f'(*({rename.class_("fz_device")}2**) ({name} + 1))',
+                    alloc = textwrap.dedent( f'''
+                        m_internal = {rename.ll_fn("fz_new_device_of_size")}(
+                                sizeof(*m_internal) + sizeof({rename.class_("fz_device")}2*)
+                                );
+                        *(({rename.class_("fz_device")}2**) (m_internal + 1)) = this;
+                        '''),
+                    ),
+                constructor_raw = True,
+                ),
+
+        fz_document = ClassExtra(
+                constructor_excludes = [
+                    'fz_new_xhtml_document_from_document',
+                    ],
+                constructor_prefixes = [
+                    'fz_open_accelerated_document',
+                    'fz_open_document',
+                    ],
+                constructors_extra = [
+                    ExtraConstructor( f'({rename.class_("pdf_document")}& pdfdocument)',
+                        f'''
+                        {{
+                            m_internal = {rename.ll_fn('fz_keep_document')}(&pdfdocument.m_internal->super);
+                        }}
+                        ''',
+                        f'/* Returns a {rename.class_("fz_document")} for pdfdocument.m_internal.super. */',
+                        ),
+                    ],
+                constructor_raw = 'default',
+                method_wrappers = [
+                    'fz_load_outline',
+                ],
+                method_wrappers_static = [
+                    'fz_new_xhtml_document_from_document',
+                    ],
+                ),
+
+        # This is a little complicated. Many of the functions that we would
+        # like to wrap to form constructors, have the same set of args. C++
+        # does not support named constructors so we differentiate between
+        # constructors with identical args using enums.
+        #
+        # Also, fz_document_writer is not reference counted so the wrapping
+        # class is not copyable or assignable, so our normal approach of making
+        # static class functions that return a newly constructed instance by
+        # value, does not work.
+        #
+        # So instead we define enums that are passed to our constructors,
+        # allowing the constructor to decide which fz_ function to use to
+        # create the new fz_document_writer.
+        #
+        # There should be no commented-out constructors in the generated code
+        # marked as 'Disabled because same args as ...'.
+        #
+        fz_document_writer = ClassExtra(
+                class_top = '''
+                    /* Used for constructor that wraps fz_ functions taking (const char *path, const char *options). */
+                    enum PathType
+                    {
+                        PathType_CBZ,
+                        PathType_DOCX,
+                        PathType_ODT,
+                        PathType_PAM_PIXMAP,
+                        PathType_PBM_PIXMAP,
+                        PathType_PCL,
+                        PathType_PCLM,
+                        PathType_PDF,
+                        PathType_PDFOCR,
+                        PathType_PGM_PIXMAP,
+                        PathType_PKM_PIXMAP,
+                        PathType_PNG_PIXMAP,
+                        PathType_PNM_PIXMAP,
+                        PathType_PPM_PIXMAP,
+                        PathType_PS,
+                        PathType_PWG,
+                        PathType_SVG,
+                    };
+
+                    /* Used for constructor that wraps fz_ functions taking (Output& out, const char *options). */
+                    enum OutputType
+                    {
+                        OutputType_CBZ,
+                        OutputType_DOCX,
+                        OutputType_ODT,
+                        OutputType_PCL,
+                        OutputType_PCLM,
+                        OutputType_PDF,
+                        OutputType_PDFOCR,
+                        OutputType_PS,
+                        OutputType_PWG,
+                    };
+
+                    /* Used for constructor that wraps fz_ functions taking (const char *format, const char *path, const char *options). */
+                    enum FormatPathType
+                    {
+                        FormatPathType_DOCUMENT,
+                        FormatPathType_TEXT,
+                    };
+                ''',
+                # These excludes should match the functions called by the
+                # extra constructors defined below. This ensures that we don't
+                # generate commented-out constructors with a comment saying
+                # 'Disabled because same args as ...'.
+                constructor_excludes = [
+                    'fz_new_cbz_writer',
+                    'fz_new_docx_writer',
+                    'fz_new_odt_writer',
+                    'fz_new_pam_pixmap_writer',
+                    'fz_new_pbm_pixmap_writer',
+                    'fz_new_pcl_writer',
+                    'fz_new_pclm_writer',
+                    'fz_new_pdfocr_writer',
+                    'fz_new_pdf_writer',
+                    'fz_new_pgm_pixmap_writer',
+                    'fz_new_pkm_pixmap_writer',
+                    'fz_new_png_pixmap_writer',
+                    'fz_new_pnm_pixmap_writer',
+                    'fz_new_ppm_pixmap_writer',
+                    'fz_new_ps_writer',
+                    'fz_new_pwg_writer',
+                    'fz_new_svg_writer',
+
+                    'fz_new_cbz_writer_with_output',
+                    'fz_new_docx_writer_with_output',
+                    'fz_new_odt_writer_with_output',
+                    'fz_new_pcl_writer_with_output',
+                    'fz_new_pclm_writer_with_output',
+                    'fz_new_pdf_writer_with_output',
+                    'fz_new_pdfocr_writer_with_output',
+                    'fz_new_ps_writer_with_output',
+                    'fz_new_pwg_writer_with_output',
+
+                    'fz_new_document_writer',
+                    'fz_new_text_writer',
+
+                    'fz_new_document_writer_with_output',
+                    'fz_new_text_writer_with_output',
+                    ],
+
+                copyable=False,
+                methods_extra = [
+                        # 2022-08-26: we used to provide a custom wrapper of
+                        # fz_begin_page(), but this is not longer necessary
+                        # because function_wrapper_class_aware_body() knows
+                        # that fz_begin_page() returns a borrowed reference.
+                        #
+                        ],
+                constructors_extra = [
+                    ExtraConstructor(
+                        '(const char *path, const char *options, PathType path_type)',
+                        f'''
+                        {{
+                            if (0) {{}}
+                            else if (path_type == PathType_CBZ)         m_internal = {rename.ll_fn( 'fz_new_cbz_writer')}(path, options);
+                            else if (path_type == PathType_DOCX)        m_internal = {rename.ll_fn( 'fz_new_docx_writer')}(path, options);
+                            else if (path_type == PathType_ODT)         m_internal = {rename.ll_fn( 'fz_new_odt_writer')}(path, options);
+                            else if (path_type == PathType_PAM_PIXMAP)  m_internal = {rename.ll_fn( 'fz_new_pam_pixmap_writer')}(path, options);
+                            else if (path_type == PathType_PBM_PIXMAP)  m_internal = {rename.ll_fn( 'fz_new_pbm_pixmap_writer')}(path, options);
+                            else if (path_type == PathType_PCL)         m_internal = {rename.ll_fn( 'fz_new_pcl_writer')}(path, options);
+                            else if (path_type == PathType_PCLM)        m_internal = {rename.ll_fn( 'fz_new_pclm_writer')}(path, options);
+                            else if (path_type == PathType_PDF)         m_internal = {rename.ll_fn( 'fz_new_pdf_writer')}(path, options);
+                            else if (path_type == PathType_PDFOCR)      m_internal = {rename.ll_fn( 'fz_new_pdfocr_writer')}(path, options);
+                            else if (path_type == PathType_PGM_PIXMAP)  m_internal = {rename.ll_fn( 'fz_new_pgm_pixmap_writer')}(path, options);
+                            else if (path_type == PathType_PKM_PIXMAP)  m_internal = {rename.ll_fn( 'fz_new_pkm_pixmap_writer')}(path, options);
+                            else if (path_type == PathType_PNG_PIXMAP)  m_internal = {rename.ll_fn( 'fz_new_png_pixmap_writer')}(path, options);
+                            else if (path_type == PathType_PNM_PIXMAP)  m_internal = {rename.ll_fn( 'fz_new_pnm_pixmap_writer')}(path, options);
+                            else if (path_type == PathType_PPM_PIXMAP)  m_internal = {rename.ll_fn( 'fz_new_ppm_pixmap_writer')}(path, options);
+                            else if (path_type == PathType_PS)          m_internal = {rename.ll_fn( 'fz_new_ps_writer')}(path, options);
+                            else if (path_type == PathType_PWG)         m_internal = {rename.ll_fn( 'fz_new_pwg_writer')}(path, options);
+                            else if (path_type == PathType_SVG)         m_internal = {rename.ll_fn( 'fz_new_svg_writer')}(path, options);
+                            else throw {rename.error_class('FZ_ERROR_ABORT')}( "Unrecognised Type value");
+                        }}
+                        ''',
+                        comment = textwrap.dedent('''
+                        /* Constructor using one of:
+                            fz_new_cbz_writer()
+                            fz_new_docx_writer()
+                            fz_new_odt_writer()
+                            fz_new_pam_pixmap_writer()
+                            fz_new_pbm_pixmap_writer()
+                            fz_new_pcl_writer()
+                            fz_new_pclm_writer()
+                            fz_new_pdf_writer()
+                            fz_new_pdfocr_writer()
+                            fz_new_pgm_pixmap_writer()
+                            fz_new_pkm_pixmap_writer()
+                            fz_new_png_pixmap_writer()
+                            fz_new_pnm_pixmap_writer()
+                            fz_new_ppm_pixmap_writer()
+                            fz_new_ps_writer()
+                            fz_new_pwg_writer()
+                            fz_new_svg_writer()
+                        */'''),
+                        ),
+                    ExtraConstructor(
+                        f'({rename.class_("fz_output")}& out, const char *options, OutputType output_type)',
+                        f'''
+                        {{
+                            /* All fz_new_*_writer_with_output() functions take
+                            ownership of the fz_output, even if they throw an
+                            exception. So we need to set out.m_internal to null
+                            here so its destructor does nothing. */
+                            ::fz_output* out2 = out.m_internal;
+                            out.m_internal = NULL;
+                            if (0) {{}}
+                            else if (output_type == OutputType_CBZ)     m_internal = {rename.ll_fn( 'fz_new_cbz_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_DOCX)    m_internal = {rename.ll_fn( 'fz_new_docx_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_ODT)     m_internal = {rename.ll_fn( 'fz_new_odt_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_PCL)     m_internal = {rename.ll_fn( 'fz_new_pcl_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_PCLM)    m_internal = {rename.ll_fn( 'fz_new_pclm_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_PDF)     m_internal = {rename.ll_fn( 'fz_new_pdf_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_PDFOCR)  m_internal = {rename.ll_fn( 'fz_new_pdfocr_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_PS)      m_internal = {rename.ll_fn( 'fz_new_ps_writer_with_output')}(out2, options);
+                            else if (output_type == OutputType_PWG)     m_internal = {rename.ll_fn( 'fz_new_pwg_writer_with_output')}(out2, options);
+                            else
+                            {{
+                                /* Ensure that out2 is dropped before we return. */
+                                {rename.ll_fn( 'fz_drop_output')}(out2);
+                                throw {rename.error_class('FZ_ERROR_ABORT')}( "Unrecognised OutputType value");
+                            }}
+                        }}
+                        ''',
+                        comment = textwrap.dedent('''
+                        /* Constructor using one of:
+                            fz_new_cbz_writer_with_output()
+                            fz_new_docx_writer_with_output()
+                            fz_new_odt_writer_with_output()
+                            fz_new_pcl_writer_with_output()
+                            fz_new_pclm_writer_with_output()
+                            fz_new_pdf_writer_with_output()
+                            fz_new_pdfocr_writer_with_output()
+                            fz_new_ps_writer_with_output()
+                            fz_new_pwg_writer_with_output()
+
+                        This constructor takes ownership of <out> -
+                        out.m_internal is set to NULL after this constructor
+                        returns so <out> must not be used again.
+                        */
+                        '''),
+                        ),
+                    ExtraConstructor(
+                        '(const char *format, const char *path, const char *options, FormatPathType format_path_type)',
+                        f'''
+                        {{
+                            if (0) {{}}
+                            else if (format_path_type == FormatPathType_DOCUMENT)   m_internal = {rename.ll_fn( 'fz_new_document_writer')}(format, path, options);
+                            else if (format_path_type == FormatPathType_TEXT)       m_internal = {rename.ll_fn( 'fz_new_text_writer')}(format, path, options);
+                            else throw {rename.error_class('FZ_ERROR_ABORT')}( "Unrecognised OutputType value");
+                        }}
+                        ''',
+                        comment = textwrap.dedent('''
+                        /* Constructor using one of:
+                            fz_new_document_writer()
+                            fz_new_text_writer()
+                        */'''),
+                        ),
+                    ExtraConstructor(
+                        f'({rename.class_("fz_output")}& out, const char *format, const char *options)',
+                        f'''
+                        {{
+                            /* Need to transfer ownership of <out>. */
+                            ::fz_output* out2 = out.m_internal;
+                            out.m_internal = NULL;
+                            m_internal = {rename.ll_fn( 'fz_new_document_writer_with_output')}(out2, format, options);
+                        }}
+                        ''',
+                        comment = textwrap.dedent('''
+                        /* Constructor using fz_new_document_writer_with_output().
+
+                        This constructor takes ownership of <out> -
+                        out.m_internal is set to NULL after this constructor
+                        returns so <out> must not be used again.
+                        */'''),
+                        ),
+                    ExtraConstructor(
+                        f'(const char *format, {rename.class_("fz_output")}& out, const char *options)',
+                        f'''
+                        {{
+                            /* Need to transfer ownership of <out>. */
+                            ::fz_output* out2 = out.m_internal;
+                            out.m_internal = NULL;
+                            m_internal = {rename.ll_fn( 'fz_new_text_writer_with_output')}(format, out2, options);
+                        }}
+                        ''',
+                        comment = textwrap.dedent('''
+                        /* Constructor using fz_new_text_writer_with_output().
+
+                        This constructor takes ownership of <out> -
+                        out.m_internal is set to NULL after this constructor
+                        returns so <out> must not be used again.
+                        */'''),
+                        ),
+                    ],
+
+                ),
+
+        fz_draw_options = ClassExtra(
+                constructors_wrappers = [
+                    'fz_parse_draw_options',
+                    ],
+                copyable=False,
+                pod='inline',
+                ),
+
+        fz_halftone = ClassExtra(
+                constructor_raw = 'default',
+                ),
+
+        fz_image = ClassExtra(
+                accessors=True,
+                ),
+
+        fz_install_load_system_font_funcs_args = ClassExtra(
+                pod = True,
+                virtual_fnptrs = dict(
+                    alloc = textwrap.dedent( f'''
+                        /*
+                        There can only be one active instance of the wrapper
+                        class so we simply keep a pointer to it in a global
+                        variable.
+                        */
+                        fz_install_load_system_font_funcs2_state = this;
+                        '''),
+                    self_ = lambda: f'({rename.class_("fz_install_load_system_font_funcs_args")}2*) fz_install_load_system_font_funcs2_state',
+                    self_n = None,
+                    ),
+                ),
+
+        fz_irect = ClassExtra(
+                constructor_prefixes = [
+                    'fz_irect_from_rect',
+                    'fz_make_irect',
+                    ],
+                pod='inline',
+                constructor_raw = True,
+                ),
+
+        fz_link = ClassExtra(
+                constructors_extra = [
+                    ExtraConstructor( f'({rename.class_("fz_rect")}& rect, const char *uri)',
+                        f'''
+                        {{
+                            m_internal = {rename.ll_fn('fz_new_link_of_size')}( sizeof(fz_link), *rect.internal(), uri);
+                        }}
+                        ''',
+                        '/* Construct by calling fz_new_link_of_size() with size=sizeof(fz_link). */',
+                        )
+                    ],
+                accessors = True,
+                iterator_next = ('', ''),
+                constructor_raw = 'default',
+                copyable = True,
+                ),
+
+        fz_location = ClassExtra(
+                constructor_prefixes = [
+                    'fz_make_location',
+                    ],
+                pod='inline',
+                constructor_raw = True,
+                ),
+
+        fz_matrix = ClassExtra(
+                constructor_prefixes = [
+                    'fz_make_matrix',
+                    ],
+                method_wrappers_static = [
+                    'fz_concat',
+                    'fz_scale',
+                    'fz_shear',
+                    'fz_rotate',
+                    'fz_translate',
+                    'fz_transform_page',
+                    ],
+                constructors_extra = [
+                    ExtraConstructor( '()',
+                        '''
+                        : a(1), b(0), c(0), d(1), e(0), f(0)
+                        {
+                        }
+                        ''',
+                        comment = '/* Constructs identity matrix (like fz_identity). */'),
+                ],
+                pod='inline',
+                constructor_raw = True,
+                ),
+
+        fz_md5 = ClassExtra(
+                pod = True,
+                constructors_extra = [
+                    ExtraConstructor(
+                        '()',
+                        f'''
+                        {{
+                            {rename.ll_fn( 'fz_md5_init')}( &m_internal);
+                        }}
+                        ''',
+                        '/* Default constructor calls md5_init(). */',
+                        )
+                    ],
+                ),
+
+        fz_outline = ClassExtra(
+                # We add various methods to give depth-first iteration of outlines.
+                #
+                constructor_prefixes = [
+                    'fz_load_outline',
+                    ],
+                accessors=True,
+                ),
+
+        fz_outline_item = ClassExtra(
+                class_top = f'''
+                        FZ_FUNCTION bool valid() const;
+                        FZ_FUNCTION const std::string& title() const;   /* Will throw if valid() is not true. */
+                        FZ_FUNCTION const std::string& uri() const;     /* Will throw if valid() is not true. */
+                        FZ_FUNCTION int is_open() const;                /* Will throw if valid() is not true. */
+                        ''',
+                class_bottom = f'''
+                        private:
+                        bool        m_valid;
+                        std::string m_title;
+                        std::string m_uri;
+                        int         m_is_open;
+                        ''',
+                constructors_extra = [
+                        ],
+                constructor_raw = 'declaration_only',
+                copyable = 'default',
+                pod = 'none',
+                extra_cpp = f'''
+                        FZ_FUNCTION {rename.class_("fz_outline_item")}::{rename.class_("fz_outline_item")}(const ::fz_outline_item* item)
+                        {{
+                            if (item)
+                            {{
+                                m_valid = true;
+                                m_title = item->title;
+                                m_uri = item->uri;
+                                m_is_open = item->is_open;
+                            }}
+                            else
+                            {{
+                                m_valid = false;
+                            }}
+                        }}
+                        FZ_FUNCTION bool {rename.class_("fz_outline_item")}::valid() const
+                        {{
+                            return m_valid;
+                        }}
+                        FZ_FUNCTION const std::string& {rename.class_("fz_outline_item")}::title() const
+                        {{
+                            if (!m_valid) throw {rename.error_class("FZ_ERROR_GENERIC")}("fz_outline_item is invalid");
+                            return m_title;
+                        }}
+                        FZ_FUNCTION const std::string& {rename.class_("fz_outline_item")}::uri() const
+                        {{
+                            if (!m_valid) throw {rename.error_class("FZ_ERROR_GENERIC")}("fz_outline_item is invalid");
+                            return m_uri;
+                        }}
+                        FZ_FUNCTION int {rename.class_("fz_outline_item")}::is_open() const
+                        {{
+                            if (!m_valid) throw {rename.error_class("FZ_ERROR_GENERIC")}("fz_outline_item is invalid");
+                            return m_is_open;
+                        }}
+                        ''',
+                ),
+
+        fz_outline_iterator = ClassExtra(
+                copyable = False,
+                methods_extra = [
+                        ExtraMethod(
+                            'int',
+                            f'{rename.method("fz_outline_iterator", "fz_outline_iterator_insert")}({rename.class_("fz_outline_item")}& item)',
+                            f'''
+                            {{
+                                /* Create a temporary fz_outline_item. */
+                                ::fz_outline_item item2;
+                                item2.title = (char*) item.title().c_str();
+                                item2.uri = (char*) item.uri().c_str();
+                                item2.is_open = item.is_open();
+                                return {rename.ll_fn("fz_outline_iterator_insert")}(m_internal, &item2);
+                            }}
+                            ''',
+                            comment = '/* Custom wrapper for fz_outline_iterator_insert(). */',
+                            ),
+                        ExtraMethod(
+                            'void',
+                            f'{rename.method("fz_outline_iterator", "fz_outline_iterator_update")}({rename.class_("fz_outline_item")}& item)',
+                            f'''
+                            {{
+                                /* Create a temporary fz_outline_item. */
+                                ::fz_outline_item item2;
+                                item2.title = (char*) item.title().c_str();
+                                item2.uri = (char*) item.uri().c_str();
+                                item2.is_open = item.is_open();
+                                return {rename.ll_fn("fz_outline_iterator_update")}(m_internal, &item2);
+                            }}
+                            ''',
+                            comment = '/* Custom wrapper for fz_outline_iterator_update(). */',
+                            ),
+                        ],
+                ),
+
+        fz_output = ClassExtra(
+                virtual_fnptrs = dict(
+                    self_ = lambda name: f'({rename.class_("fz_output")}2*) {name}',
+                    alloc = f'm_internal = {rename.ll_fn("fz_new_output")}(0 /*bufsize*/, this /*state*/, nullptr /*write*/, nullptr /*close*/, nullptr /*drop*/);\n',
+                    ),
+                constructor_raw = 'default',
+                constructor_excludes = [
+                    # These all have the same prototype, so are used by
+                    # constructors_extra below.
+                    'fz_new_asciihex_output',
+                    'fz_new_ascii85_output',
+                    'fz_new_rle_output',
+                    ],
+                constructors_extra = [
+                    ExtraConstructor( '(Fixed out)',
+                        f'''
+                        {{
+                            if (0)  {{}}
+                            else if (out == Fixed_STDOUT) {{
+                                m_internal = {rename.ll_fn('fz_stdout')}();
+                            }}
+                            else if (out == Fixed_STDERR) {{
+                                m_internal = {rename.ll_fn('fz_stderr')}();
+                            }}
+                            else {{
+                                throw {rename.error_class('FZ_ERROR_ABORT')}("Unrecognised Fixed value");
+                            }}
+                        }}
+                        ''',
+                        '/* Uses fz_stdout() or fz_stderr(). */',
+                        # Note that it's ok to call fz_drop_output() on fz_stdout and fz_stderr.
+                        ),
+                    ExtraConstructor(
+                        f'(const {rename.class_("fz_output")}& chain, Filter filter)',
+                        f'''
+                        {{
+                            if (0)  {{}}
+                            else if (filter == Filter_HEX) {{
+                                m_internal = {rename.ll_fn('fz_new_asciihex_output')}(chain.m_internal);
+                            }}
+                            else if (filter == Filter_85) {{
+                                m_internal = {rename.ll_fn('fz_new_ascii85_output')}(chain.m_internal);
+                            }}
+                            else if (filter == Filter_RLE) {{
+                                m_internal = {rename.ll_fn('fz_new_rle_output')}(chain.m_internal);
+                            }}
+                            else {{
+                                throw {rename.error_class('FZ_ERROR_ABORT')}("Unrecognised Filter value");
+                            }}
+                        }}
+                        ''',
+                        comment = '/* Calls one of: fz_new_asciihex_output(), fz_new_ascii85_output(), fz_new_rle_output(). */',
+                        ),
+                    ],
+                class_top = '''
+                    enum Fixed
+                    {
+                        Fixed_STDOUT=1,
+                        Fixed_STDERR=2,
+                    };
+                    enum Filter
+                    {
+                        Filter_HEX,
+                        Filter_85,
+                        Filter_RLE,
+                    };
+                    '''
+                    ,
+                copyable=False, # No fz_keep_output() fn?
+                ),
+
+        fz_page = ClassExtra(
+                constructor_prefixes = [
+                    'fz_load_page',
+                    'fz_load_chapter_page',
+                    ],
+                constructors_extra = [
+                    ExtraConstructor( f'({rename.class_("pdf_page")}& pdfpage)',
+                        f'''
+                        {{
+                            m_internal = {rename.ll_fn('fz_keep_page')}(&pdfpage.m_internal->super);
+                        }}
+                        ''',
+                        f'/* Return {rename.class_("fz_page")} for pdfpage.m_internal.super. */',
+                        ),
+                    ],
+                methods_extra = [
+                    ExtraMethod(
+                        f'{rename.class_("fz_document")}',
+                        'doc()',
+                        f'''
+                        {{
+                            return {rename.class_("fz_document")}( {rename.ll_fn('fz_keep_document')}( m_internal->doc));
+                        }}
+                        ''',
+                        f'/* Returns wrapper for .doc member. */',
+                        ),
+                ],
+                constructor_raw = True,
+                ),
+
+        fz_path_walker = ClassExtra(
+                constructor_raw = 'default',
+                virtual_fnptrs = dict(
+                    self_ = lambda name: f'*({rename.class_("fz_path_walker")}2**) ((fz_path_walker*) {name} + 1)',
+                    alloc = textwrap.dedent( f'''
+                        m_internal = (::fz_path_walker*) {rename.ll_fn("fz_calloc")}(
+                                1,
+                                sizeof(*m_internal) + sizeof({rename.class_("fz_path_walker")}2*)
+                                );
+                        *({rename.class_("fz_path_walker")}2**) (m_internal + 1) = this;
+                        '''),
+                    free = f'{rename.ll_fn("fz_free")}(m_internal);\n',
+                    comment = textwrap.dedent(f'''
+                            /*
+                            We require that the `void* arg` passed to callbacks
+                            is the original `fz_path_walker*`. So, for example,
+                            class-aware wrapper mupdf::fz_walk_path() should be
+                            called like:
+
+                                mupdf.FzPath path = ...;
+                                struct Walker : mupdf.FzPathWalker2 {...};
+                                Walker walker(...);
+                                mupdf::fz_walk_path(path, walker, walker.m_internal);
+                            */
+                            ''')
+                    ),
+                ),
+
+        fz_pcl_options = ClassExtra(
+                constructors_wrappers = [
+                    'fz_parse_pcl_options',
+                    ],
+                copyable=False,
+                ),
+
+        fz_pclm_options = ClassExtra(
+                constructor_prefixes = [
+                    'fz_parse_pclm_options',
+                    ],
+                copyable=False,
+                constructors_extra = [
+                    ExtraConstructor( '(const char *args)',
+                        f'''
+                        {{
+                            {rename.ll_fn('fz_parse_pclm_options')}(m_internal, args);
+                        }}
+                        ''',
+                        '/* Construct using fz_parse_pclm_options(). */',
+                        )
+                    ],
+                ),
+
+        fz_pdfocr_options = ClassExtra(
+                pod = 'inline',
+                methods_extra = [
+                    ExtraMethod(
+                        'void',
+                        'language_set2(const char* language)',
+                        f'''
+                        {{
+                            fz_strlcpy(this->language, language, sizeof(this->language));
+                        }}
+                        ''',
+                        '/* Copies <language> into this->language, truncating if necessary. */',
+                        ),
+                    ExtraMethod(
+                        'void',
+                        'datadir_set2(const char* datadir)',
+                        f'''
+                        {{
+                            fz_strlcpy(this->datadir, datadir, sizeof(this->datadir));
+                        }}
+                        ''',
+                        '/* Copies <datadir> into this->datadir, truncating if necessary. */',
+                        ),
+                    ],
+                ),
+
+        fz_pixmap = ClassExtra(
+                constructor_raw = True,
+                accessors = True,
+                ),
+
+        fz_point = ClassExtra(
+                method_wrappers_static = [
+                    'fz_transform_point',
+                    'fz_transform_point_xy',
+                    'fz_transform_vector',
+
+                    ],
+                constructors_extra = [
+                    ExtraConstructor( '(float x, float y)',
+                        '''
+                        : x(x), y(y)
+                        {
+                        }
+                        ''',
+                        comment = '/* Construct using specified values. */',
+                        ),
+                        ],
+                methods_extra = [
+                    ExtraMethod(
+                        f'{rename.class_("fz_point")}&',
+                        f'transform(const {rename.class_("fz_matrix")}& m)',
+                        '''
+                        {
+                            double  old_x = x;
+                            x = old_x * m.a + y * m.c + m.e;
+                            y = old_x * m.b + y * m.d + m.f;
+                            return *this;
+                        }
+                        ''',
+                        comment = '/* Post-multiply *this by <m> and return *this. */',
+                        ),
+                ],
+                pod='inline',
+                constructor_raw = True,
+                ),
+
+        fz_pwg_options = ClassExtra(
+                pod=True,
+                ),
+
+        fz_quad = ClassExtra(
+                constructor_prefixes = [
+                    'fz_transform_quad',
+                    'fz_quad_from_rect'
+                    ],
+                pod='inline',
+                constructor_raw = True,
+                ),
+
+        fz_rect = ClassExtra(
+                constructor_prefixes = [
+                    'fz_transform_rect',
+                    'fz_bound_display_list',
+                    'fz_rect_from_irect',
+                    'fz_rect_from_quad',
+                    ],
+                method_wrappers_static = [
+                    'fz_intersect_rect',
+                    'fz_union_rect',
+                    ],
+                constructors_extra = [
+                    ExtraConstructor(
+                        '(double x0, double y0, double x1, double y1)',
+                        '''
+                        :
+                        x0(x0),
+                        x1(x1),
+                        y0(y0),
+                        y1(y1)
+                        {
+                        }
+                        ''',
+                        comment = '/* Construct from specified values. */',
+                        ),
+                    ExtraConstructor(
+                        f'(const {rename.class_("fz_rect")}& rhs)',
+                        '''
+                        :
+                        x0(rhs.x0),
+                        y0(rhs.y0),
+                        x1(rhs.x1),
+                        y1(rhs.y1)
+                        {
+                        }
+                        ''',
+                        comment = '/* Copy constructor using plain copy. */',
+                        ),
+                    ExtraConstructor( '(Fixed fixed)',
+                        f'''
+                        {{
+                            if (0)  {{}}
+                            else if (fixed == Fixed_UNIT)       *this->internal() = {rename.c_fn('fz_unit_rect')};
+                            else if (fixed == Fixed_EMPTY)      *this->internal() = {rename.c_fn('fz_empty_rect')};
+                            else if (fixed == Fixed_INFINITE)   *this->internal() = {rename.c_fn('fz_infinite_rect')};
+                            else throw {rename.error_class('FZ_ERROR_ABORT')}( "Unrecognised From value");
+                        }}
+                        ''',
+                        comment = '/* Construct from fz_unit_rect, fz_empty_rect or fz_infinite_rect. */',
+                        ),
+                    ],
+                methods_extra = [
+                    ExtraMethod(
+                        'void',
+                        f'transform(const {rename.class_("fz_matrix")}& m)',
+                        f'''
+                        {{
+                            *(::fz_rect*) &this->x0 = {rename.c_fn('fz_transform_rect')}(*(::fz_rect*) &this->x0, *(::fz_matrix*) &m.a);
+                        }}
+                        ''',
+                        comment = '/* Transforms *this using fz_transform_rect() with <m>. */',
+                        ),
+                    ExtraMethod( 'bool', 'contains(double x, double y)',
+                        '''
+                        {
+                            if (is_empty()) {
+                                return false;
+                            }
+                            return true
+                                    && x >= x0
+                                    && x < x1
+                                    && y >= y0
+                                    && y < y1
+                                    ;
+                        }
+                        ''',
+                        comment = '/* Convenience method using fz_contains_rect(). */',
+                        ),
+                    ExtraMethod( 'bool', f'contains({rename.class_("fz_rect")}& rhs)',
+                        f'''
+                        {{
+                            return {rename.c_fn('fz_contains_rect')}(*(::fz_rect*) &x0, *(::fz_rect*) &rhs.x0);
+                        }}
+                        ''',
+                        comment = '/* Uses fz_contains_rect(*this, rhs). */',
+                        ),
+                    ExtraMethod( 'bool', 'is_empty()',
+                        f'''
+                        {{
+                            return {rename.c_fn('fz_is_empty_rect')}(*(::fz_rect*) &x0);
+                        }}
+                        ''',
+                        comment = '/* Uses fz_is_empty_rect(). */',
+                        ),
+                    ExtraMethod( 'void', f'union_({rename.class_("fz_rect")}& rhs)',
+                        f'''
+                        {{
+                            *(::fz_rect*) &x0 = {rename.c_fn('fz_union_rect')}(*(::fz_rect*) &x0, *(::fz_rect*) &rhs.x0);
+                        }}
+                        ''',
+                        comment = '/* Updates *this using fz_union_rect(). */',
+                        ),
+                    ],
+                pod='inline',
+                constructor_raw = True,
+                copyable = True,
+                class_top = '''
+                    enum Fixed
+                    {
+                        Fixed_UNIT,
+                        Fixed_EMPTY,
+                        Fixed_INFINITE,
+                    };
+                    ''',
+                ),
+
+        fz_separations = ClassExtra(
+                constructor_raw = 'default',
+                opaque = True,
+                ),
+
+        fz_shade = ClassExtra(
+                methods_extra = [
+                    ExtraMethod( 'void',
+                        f'{rename.method( "fz_shade", "fz_paint_shade_no_cache")}('
+                            + f' const {rename.class_("fz_colorspace")}& override_cs'
+                            + f', {rename.class_("fz_matrix")}& ctm'
+                            + f', const {rename.class_("fz_pixmap")}& dest'
+                            + f', {rename.class_("fz_color_params")}& color_params'
+                            + f', {rename.class_("fz_irect")}& bbox'
+                            + f', const {rename.class_("fz_overprint")}& eop'
+                            + f')'
+                            ,
+                        f'''
+                        {{
+                            return {rename.ll_fn('fz_paint_shade')}(
+                                    this->m_internal,
+                                    override_cs.m_internal,
+                                    *(::fz_matrix*) &ctm.a,
+                                    dest.m_internal,
+                                    *(::fz_color_params*) &color_params.ri,
+                                    *(::fz_irect*) &bbox.x0,
+                                    eop.m_internal,
+                                    NULL /*cache*/
+                                    );
+                        }}
+                        ''',
+                        comment = f'/* Extra wrapper for fz_paint_shade(), passing cache=NULL. */',
+                        ),
+                ],
+                ),
+
+        fz_shade_color_cache = ClassExtra(
+                ),
+
+        # Our wrappers of the fz_stext_* structs all have a default copy
+        # constructor - there are no fz_keep_stext_*() functions.
+        #
+        # We define explicit accessors for fz_stext_block::u.i.* because SWIG
+        # does not handle nested unions.
+        #
+        fz_stext_block = ClassExtra(
+                iterator_next = ('u.t.first_line', 'u.t.last_line'),
+                copyable='default',
+                methods_extra = [
+                    ExtraMethod( f'{rename.class_("fz_matrix")}', 'i_transform()',
+                        f'''
+                        {{
+                            if (m_internal->type != FZ_STEXT_BLOCK_IMAGE) {{
+                                throw std::runtime_error("Not an image");
+                            }}
+                            return m_internal->u.i.transform;
+                        }}
+                        ''',
+                        comment=f'/* Returns m_internal.u.i.transform if m_internal->type is FZ_STEXT_BLOCK_IMAGE, else throws. */',
+                        ),
+                    ExtraMethod( f'{rename.class_("fz_image")}', 'i_image()',
+                        f'''
+                        {{
+                            if (m_internal->type != FZ_STEXT_BLOCK_IMAGE) {{
+                                throw std::runtime_error("Not an image");
+                            }}
+                            return {rename.class_("fz_image")}({rename.ll_fn('fz_keep_image')}(m_internal->u.i.image));
+                        }}
+                        ''',
+                        comment=f'/* Returns m_internal.u.i.image if m_internal->type is FZ_STEXT_BLOCK_IMAGE, else throws. */',
+                        ),
+                        ],
+                ),
+
+        fz_stext_char = ClassExtra(
+                copyable='default',
+                ),
+
+        fz_stext_line = ClassExtra(
+                iterator_next = ('first_char', 'last_char'),
+                copyable='default',
+                constructor_raw=True,
+                ),
+
+        fz_stext_options = ClassExtra(
+                constructors_extra = [
+                    ExtraConstructor( '(int flags, float scale=1.0)',
+                        '''
+                        :
+                        flags(flags),
+                        scale(scale)
+                        {
+                            assert(!(flags & FZ_STEXT_CLIP_RECT));
+                        }
+                        ''',
+                        comment = '/* Construct with .flags, .scale but no clip. */',
+                        ),
+                    ExtraConstructor( '(int flags, fz_rect clip, float scale=1.0)',
+                        '''
+                        :
+                        flags(flags | FZ_STEXT_CLIP_RECT),
+                        scale(scale),
+                        clip(clip)
+                        {
+                        }
+                        ''',
+                        comment =
+                                '/* Construct with .flags, .scale and .clip; FZ_STEXT_CLIP_RECT\n'
+                                'is automatically set in .flags. */'
+                                ,
+                        ),
+                    ],
+                pod='inline',
+                ),
+
+        fz_stext_page = ClassExtra(
+                methods_extra = [
+                    ExtraMethod(
+                        'std::string',
+                        f'{rename.method( "fz_stext_page", "fz_copy_selection")}('
+                            + f'{rename.class_("fz_point")}& a'
+                            + f', {rename.class_("fz_point")}& b'
+                            + f', int crlf'
+                            + f')',
+                        f'''
+                        {{
+                            char* text = {rename.ll_fn('fz_copy_selection')}(m_internal, *(::fz_point *) &a.x, *(::fz_point *) &b.x, crlf);
+                            std::string ret(text);
+                            {rename.ll_fn('fz_free')}(text);
+                            return ret;
+                        }}
+                        ''',
+                        comment = f'/* Wrapper for fz_copy_selection() that returns std::string. */',
+                        ),
+                    ExtraMethod(
+                        'std::string',
+                        f'{rename.method( "fz_stext_page", "fz_copy_rectangle")}({rename.class_("fz_rect")}& area, int crlf)',
+                        f'''
+                        {{
+                            char* text = {rename.ll_fn('fz_copy_rectangle')}(m_internal, *(::fz_rect*) &area.x0, crlf);
+                            std::string ret(text);
+                            {rename.ll_fn('fz_free')}(text);
+                            return ret;
+                        }}
+                        ''',
+                        comment = f'/* Wrapper for fz_copy_rectangle() that returns a std::string. */',
+                        ),
+                    ExtraMethod(
+                        f'std::vector<{rename.class_("fz_quad")}>',
+                        f'{rename.method( "fz_stext_page", "search_stext_page")}(const char* needle, int *hit_mark, int max_quads)',
+                        f'''
+                        {{
+                            std::vector<{rename.class_("fz_quad")}> ret(max_quads);
+                            int n = {rename.ll_fn('fz_search_stext_page')}(m_internal, needle, hit_mark, ret[0].internal(), max_quads);
+                            ret.resize(n);
+                            return ret;
+                        }}
+                        ''',
+                        '/* Wrapper for fz_search_stext_page() that returns std::vector of Quads. */',
+                        )
+                    ],
+                iterator_next = ('first_block', 'last_block'),
+                copyable=False,
+                constructor_raw = True,
+                ),
+
+        fz_text_span = ClassExtra(
+                copyable=False,
+                methods_extra = [
+                    # We provide class-aware accessors where possible. (Some
+                    # types' wrapper classes are not copyable so we can't do
+                    # this for all data).
+                    ExtraMethod(
+                        f'{rename.class_("fz_font")}',
+                        f'font()',
+                        f'''
+                        {{
+                            return {rename.class_("fz_font")}( ll_fz_keep_font( m_internal->font));
+                        }}
+                        ''',
+                        f'/* Gives class-aware access to m_internal->font. */',
+                        ),
+                    ExtraMethod(
+                        f'{rename.class_("fz_matrix")}',
+                        f'trm()',
+                        f'''
+                        {{
+                            return {rename.class_("fz_matrix")}( m_internal->trm);
+                        }}
+                        ''',
+                        f'/* Gives class-aware access to m_internal->trm. */',
+                        ),
+                    ExtraMethod(
+                        f'fz_text_item&',
+                        f'items( int i)',
+                        f'''
+                        {{
+                            assert( i < m_internal->len);
+                            return m_internal->items[i];
+                        }}
+                        ''',
+                        '''
+                        /* Gives access to m_internal->items[i].
+                        Returned reference is only valid as long as `this`.
+                        Provided mainly for use by SWIG bindings.
+                        */
+                        ''',
+                        ),
+                    ],
+                ),
+
+        fz_stream = ClassExtra(
+                constructor_prefixes = [
+                    'fz_open_file',
+                    'fz_open_memory',
+                    ],
+                constructors_extra = [
+                    ExtraConstructor( '(const std::string& filename)',
+                    f'''
+                    : m_internal({rename.ll_fn('fz_open_file')}(filename.c_str()))
+                    {{
+                    }}
+                    ''',
+                    comment = '/* Construct using fz_open_file(). */',
+                    )
+                    ],
+                ),
+
+        fz_story_element_position = ClassExtra(
+                pod='inline',
+                ),
+
+        fz_transition = ClassExtra(
+                pod='inline',
+                constructor_raw = True,
+                ),
+
+        pdf_annot = ClassExtra(
+                constructor_raw = 'default',
+                ),
+
+        pdf_clean_options = ClassExtra(
+                constructors_extra = [
+                    ExtraConstructor( '()',
+                        f'''
+                        {{
+                            /* Use memcpy() otherwise we get 'invalid array assignment' errors. */
+                            memcpy(&this->internal()->write, &pdf_default_write_options, sizeof(this->internal()->write));
+                            memset(&this->internal()->image, 0, sizeof(this->internal()->image));
+                        }}
+                        ''',
+                        comment = '/* Default constructor, makes copy of pdf_default_write_options. */'
+                        ),
+                    ExtraConstructor(
+                        f'(const {rename.class_("pdf_clean_options")}& rhs)',
+                        f'''
+                        {{
+                            *this = rhs;
+                        }}
+                        ''',
+                        comment = '/* Copy constructor using raw memcopy(). */'
+                        ),
+                ],
+                methods_extra = [
+                    ExtraMethod(
+                        f'{rename.class_("pdf_clean_options")}&',
+                        f'operator=(const {rename.class_("pdf_clean_options")}& rhs)',
+                        f'''
+                        {{
+                            memcpy(this->internal(), rhs.internal(), sizeof(*this->internal()));
+                            return *this;
+                        }}
+                        ''',
+                        comment = '/* Assignment using plain memcpy(). */',
+                        ),
+                    ExtraMethod(
+                        f'void',
+                        f'write_opwd_utf8_set(const std::string& text)',
+                        f'''
+                        {{
+                            size_t len = std::min(text.size(), sizeof(write.opwd_utf8) - 1);
+                            memcpy(write.opwd_utf8, text.c_str(), len);
+                            write.opwd_utf8[len] = 0;
+                        }}
+                        ''',
+                        '/* Copies <text> into write.opwd_utf8[]. */',
+                        ),
+                    ExtraMethod(
+                        f'void',
+                        f'write_upwd_utf8_set(const std::string& text)',
+                        f'''
+                        {{
+                            size_t len = std::min(text.size(), sizeof(write.upwd_utf8) - 1);
+                            memcpy(write.upwd_utf8, text.c_str(), len);
+                            write.upwd_utf8[len] = 0;
+                        }}
+                        ''',
+                        '/* Copies <text> into upwd_utf8[]. */',
+                        ),
+                ],
+                pod = 'inline',
+                copyable = 'default',
+                ),
+
+        pdf_document = ClassExtra(
+                constructor_prefixes = [
+                    'pdf_open_document',
+                    'pdf_create_document',
+                    'pdf_document_from_fz_document',
+                    ],
+                methods_extra = [
+                    ExtraMethod(
+                        f'{rename.class_("fz_document")}',
+                        'super()',
+                        f'''
+                        {{
+                            return {rename.class_("fz_document")}( {rename.ll_fn('fz_keep_document')}( &m_internal->super));
+                        }}
+                        ''',
+                        f'/* Returns wrapper for .super member. */',
+                        ),
+                    ],
+                ),
+
+        pdf_filter_factory = ClassExtra(
+                pod = 'inline',
+                virtual_fnptrs = dict(
+                    self_ = lambda name: f'({rename.class_("pdf_filter_factory")}2*) {name}',
+                    self_n = 6,
+                    alloc = f'this->options = this;\n',
+                    ),
+                ),
+
+        pdf_filter_options = ClassExtra(
+                pod = 'inline',
+                # We don't need to allocate extra space, and because we are a
+                # POD class, we can simply let our default constructor run.
+                #
+                # this->opaque is passed as arg[2].
+                #
+                virtual_fnptrs = dict(
+                        self_ = lambda name: f'({rename.class_("pdf_filter_options")}2*) {name}',
+                        self_n = 2,
+                        alloc = f'this->opaque = this;\n',
+                        ),
+                constructors_extra = [
+                    ExtraConstructor( '()',
+                        f'''
+                        {{
+                            this->recurse = 0;
+                            this->instance_forms = 0;
+                            this->ascii = 0;
+                            this->opaque = nullptr;
+                            this->complete = nullptr;
+                            this->filters = nullptr;
+                            pdf_filter_factory eof = {{ nullptr, nullptr}};
+                            m_filters.push_back( eof);
+                            this->newlines = 0;
+                        }}
+                        ''',
+                        comment = '/* Default constructor initialises all fields to null/zero. */',
+                    )
+                    ],
+                methods_extra = [
+                    ExtraMethod(
+                            'void',
+                            f'add_factory( const pdf_filter_factory& factory)',
+                            textwrap.dedent( f'''
+                                {{
+                                    this->m_filters.back() = factory;
+                                    pdf_filter_factory eof = {{ nullptr, nullptr}};
+                                    this->m_filters.push_back( eof);
+                                    this->filters = &this->m_filters[0];
+                                }}
+                                '''),
+                            comment = f'/* Appends `factory` to internal vector and updates this->filters. */',
+                            ),
+                    ],
+                class_bottom = textwrap.dedent( f'''
+                    std::vector< pdf_filter_factory> m_filters;
+                    '''),
+                ),
+
+        pdf_lexbuf = ClassExtra(
+                constructors_extra = [
+                    ExtraConstructor( '(int size)',
+                        f'''
+                        {{
+                            m_internal = new ::pdf_lexbuf;
+                            {rename.ll_fn('pdf_lexbuf_init')}(m_internal, size);
+                        }}
+                        ''',
+                        comment = '/* Constructor that calls pdf_lexbuf_init(size). */',
+                        ),
+                    ],
+                methods_extra = [
+                    ExtraMethod(
+                        '',
+                        '~()',
+                        f'''
+                        {{
+                            {rename.ll_fn('pdf_lexbuf_fin')}(m_internal);
+                            delete m_internal;
+                        }}
+                        ''',
+                        comment = '/* Destructor that calls pdf_lexbuf_fin(). */',
+                        ),
+                    ],
+                ),
+
+        pdf_layer_config = ClassExtra(
+                pod = 'inline',
+                ),
+
+        pdf_layer_config_ui = ClassExtra(
+                pod = 'inline',
+                constructors_extra = [
+                    ExtraConstructor( '()',
+                        f'''
+                        {{
+                            this->text = nullptr;
+                            this->depth = 0;
+                            this->type = PDF_LAYER_UI_LABEL;
+                            this->selected = 0;
+                            this->locked = 0;
+                        }}
+                        ''',
+                        comment = '/* Default constructor sets .text to null, .type to PDF_LAYER_UI_LABEL, and other fields to zero. */',
+                        ),
+                    ],
+                ),
+
+        pdf_obj = ClassExtra(
+                constructor_raw = 'default',
+                methods_extra = [
+                    ExtraMethod(
+                        f'{rename.class_("pdf_obj")}',
+                        f'{rename.method( "pdf_obj", "pdf_dict_get")}(int key)',
+                        f'''
+                        {{
+                            ::pdf_obj* temp = {rename.ll_fn('pdf_dict_get')}(this->m_internal, (::pdf_obj*)(uintptr_t) key);
+                            {rename.ll_fn('pdf_keep_obj')}(temp);
+                            auto ret = {rename.class_('pdf_obj')}(temp);
+                            return ret;
+                        }}
+                        ''',
+                        comment = '/* Typesafe wrapper for looking up things such as PDF_ENUM_NAME_Annots. */',
+                        overload=True,
+                        ),
+                    ExtraMethod(
+                        f'std::string',
+                        f'{rename.method( "pdf_obj", "pdf_load_field_name2")}()',
+                        f'''
+                        {{
+                            return {rename.namespace_fn('pdf_load_field_name2')}( *this);
+                        }}
+                        ''',
+                        comment = f'/* Alternative to `{rename.fn("pdf_load_field_name")}()` that returns a std::string. */',
+                        ),
+                    ]
+                ),
+
+        pdf_page = ClassExtra(
+                methods_extra = [
+                    ExtraMethod(
+                        f'{rename.class_("fz_page")}',
+                        'super()',
+                        f'''
+                        {{
+                            return {rename.class_("fz_page")}( {rename.ll_fn('fz_keep_page')}( &m_internal->super));
+                        }}
+                        ''',
+                        f'/* Returns wrapper for .super member. */',
+                        ),
+                    ExtraMethod(
+                        f'{rename.class_("pdf_document")}',
+                        'doc()',
+                        f'''
+                        {{
+                            return {rename.class_("pdf_document")}( {rename.ll_fn('pdf_keep_document')}( m_internal->doc));
+                        }}
+                        ''',
+                        f'/* Returns wrapper for .doc member. */',
+                        ),
+                    ExtraMethod(
+                        f'{rename.class_("pdf_obj")}',
+                        'obj()',
+                        f'''
+                        {{
+                            return {rename.class_("pdf_obj")}( {rename.ll_fn('pdf_keep_obj')}( m_internal->obj));
+                        }}
+                        ''',
+                        f'/* Returns wrapper for .obj member. */',
+                        ),
+                    ],
+                ),
+
+        pdf_image_rewriter_options = ClassExtra(
+                pod = 'inline',
+                copyable = 'default',
+                ),
+
+        pdf_processor = ClassExtra(
+                virtual_fnptrs = dict(
+                    self_ = lambda name: f'(*({rename.class_("pdf_processor")}2**) ({name} + 1))',
+                    alloc = textwrap.dedent( f'''
+                        m_internal = (::pdf_processor*) {rename.ll_fn("pdf_new_processor")}(
+                                sizeof(*m_internal)
+                                + sizeof({rename.class_("pdf_processor")}2*)
+                                );
+                        *(({rename.class_("pdf_processor")}2**) (m_internal + 1)) = this;
+                        '''),
+                    ),
+                ),
+
+        pdf_recolor_options = ClassExtra(
+                pod = 'inline',
+                ),
+
+        pdf_redact_options = ClassExtra(
+                pod = 'inline',
+                ),
+
+        pdf_sanitize_filter_options = ClassExtra(
+                pod = 'inline',
+                # this->opaque is passed as arg[1].
+                virtual_fnptrs = dict(
+                        self_ = lambda name: f'({rename.class_("pdf_sanitize_filter_options")}2*) {name}',
+                        alloc = f'this->opaque = this;\n',
+                        ),
+                ),
+
+        pdf_write_options = ClassExtra(
+                constructors_extra = [
+                    ExtraConstructor( '()',
+                        f'''
+                        {{
+                            /* Use memcpy() otherwise we get 'invalid array assignment' errors. */
+                            memcpy(this->internal(), &pdf_default_write_options, sizeof(*this->internal()));
+                        }}
+                        ''',
+                        comment = '/* Default constructor, makes copy of pdf_default_write_options. */'
+                        ),
+                    ExtraConstructor(
+                        f'(const {rename.class_("pdf_write_options")}& rhs)',
+                        f'''
+                        {{
+                            *this = rhs;
+                        }}
+                        ''',
+                        comment = '/* Copy constructor using raw memcopy(). */'
+                        ),
+                ],
+                methods_extra = [
+                    ExtraMethod(
+                        f'{rename.class_("pdf_write_options")}&',
+                        f'operator=(const {rename.class_("pdf_write_options")}& rhs)',
+                        f'''
+                        {{
+                            memcpy(this->internal(), rhs.internal(), sizeof(*this->internal()));
+                            return *this;
+                        }}
+                        ''',
+                        comment = '/* Assignment using plain memcpy(). */',
+                        ),
+                    ExtraMethod(
+                        # Would prefer to call this opwd_utf8_set() but
+                        # this conflicts with SWIG-generated accessor for
+                        # opwd_utf8.
+                        f'void',
+                        f'opwd_utf8_set_value(const std::string& text)',
+                        f'''
+                        {{
+                            size_t len = std::min(text.size(), sizeof(opwd_utf8) - 1);
+                            memcpy(opwd_utf8, text.c_str(), len);
+                            opwd_utf8[len] = 0;
+                        }}
+                        ''',
+                        '/* Copies <text> into opwd_utf8[]. */',
+                        ),
+                    ExtraMethod(
+                        f'void',
+                        f'upwd_utf8_set_value(const std::string& text)',
+                        f'''
+                        {{
+                            size_t len = std::min(text.size(), sizeof(upwd_utf8) - 1);
+                            memcpy(upwd_utf8, text.c_str(), len);
+                            upwd_utf8[len] = 0;
+                        }}
+                        ''',
+                        '/* Copies <text> into upwd_utf8[]. */',
+                        ),
+                    ],
+                pod = 'inline',
+                copyable = 'default',
+                )
+        )