We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Saving with the ".jp2" extension results in a vertically flipped image
### Describe the bug

Images get flipped when saving as jp2 files.
This was my fault when I added this feature 🎉

### To Reproduce

```
from sunpy.map import Map
m = Map("https://helioviewer.org/jp2/AIA/2024/03/08/304/2024_03_08__00_00_05_123__SDO_AIA_AIA_304.jp2")
m.peek()
m.save("test.jp2")
flipped = Map("test.jp2")
flipped.peek()
```

See screenshots below


### Screenshots

`m.peek()`
<img width="634" alt="image" src="https://github.com/sunpy/sunpy/assets/94071409/fc6f1648-9bd3-4e11-a726-9b2d4810e42e">

`flipped.peek()`
<img width="634" alt="image" src="https://github.com/sunpy/sunpy/assets/94071409/04c66615-05b3-4776-81c6-7aefa3c9ca5f">


### System Details


>>> import sunpy
>>> sunpy.util.system_info()
==============================
sunpy Installation Information
==============================

General
#######
OS: Mac OS 14.3.1
Arch: 64bit, (arm)
sunpy: 4.0.dev2573+gf79e5d92d
Installation path: sunpy.egg-info

Required Dependencies
#####################
astropy: 6.0.0
numpy: 1.26.4
packaging: 23.2
parfive: 2.0.2
pyerfa: 2.0.1.1
requests: 2.31.0

Optional Dependencies
#####################
sunpy: 4.0.dev2573+gf79e5d92d

### Installation method

git checkout

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sunpy/io/_jp2.py`
Content:
```
1 """
2 This module provides a JPEG 2000 file reader for internal use.
3 
4 .. warning::
5 
6     ``sunpy.io.jp2`` is deprecated, and will be removed in sunpy 4.1. This is
7     because it was designed for internal use only.
8 """
9 import os
10 from xml.etree import ElementTree as ET
11 
12 import numpy as np
13 
14 from sunpy.io.header import FileHeader
15 from sunpy.util.io import HDPair, string_is_float
16 from sunpy.util.xml import xml_to_dict
17 
18 __all__ = ['read', 'get_header', 'write']
19 
20 
21 def read(filepath, **kwargs):
22     """
23     Reads a JPEG2000 file.
24 
25     Parameters
26     ----------
27     filepath : `str`
28         The file to be read.
29     **kwargs : `dict`
30         Unused.
31 
32     Returns
33     -------
34     `list`
35         A list of (data, header) tuples.
36     """
37     # Put import here to speed up sunpy.io import time
38     from glymur import Jp2k
39 
40     header = get_header(filepath)
41     data = Jp2k(filepath)[...][::-1]
42     return [HDPair(data, header[0])]
43 
44 
45 def get_header(filepath):
46     """
47     Reads the header from the file.
48 
49     Parameters
50     ----------
51     filepath : `str`
52         The file to be read.
53 
54     Returns
55     -------
56     `list`
57         A list of one header read from the file.
58     """
59     # Put import here to speed up sunpy.io import time
60     from glymur import Jp2k
61     jp2 = Jp2k(filepath)
62     xml_box = [box for box in jp2.box if box.box_id == 'xml ']
63     xmlstring = ET.tostring(xml_box[0].xml.find('fits'))
64     pydict = xml_to_dict(xmlstring)["fits"]
65 
66     # Fix types
67     for k, v in pydict.items():
68         if v.isdigit():
69             pydict[k] = int(v)
70         elif string_is_float(v):
71             pydict[k] = float(v)
72 
73     # Remove newlines from comment
74     if 'comment' in pydict:
75         pydict['comment'] = pydict['comment'].replace("\n", "")
76 
77     # Is this file a Helioviewer Project JPEG2000 file?
78     pydict['helioviewer'] = xml_box[0].xml.find('helioviewer') is not None
79 
80     return [FileHeader(pydict)]
81 
82 
83 def header_to_xml(header):
84     """
85     Converts image header metadata into an XML Tree that can be inserted into
86     a JP2 file header.
87 
88     Parameters
89     ----------
90     header : `MetaDict`
91         A header dictionary to convert to xml.
92 
93     Returns
94     ----------
95     `lxml.etree._Element`
96         A fits element where each child is an xml element
97         in the form <key>value</key> derived from the key/value
98         pairs in the given header dictionary
99     """
100     # glymur uses lxml and will crash if trying to use
101     # python's builtin xml.etree
102     import lxml.etree as ET
103 
104     fits = ET.Element("fits")
105 
106     already_added = set()
107     for key in header:
108         # Some headers span multiple lines and get duplicated as keys
109         # header.get will appropriately return all data, so if we see
110         # a key again, we can assume it was already added to the xml tree.
111         if (key in already_added):
112             continue
113 
114         # Add to the set so we don't duplicate entries
115         already_added.add(key)
116 
117         el = ET.SubElement(fits, key)
118         data = header.get(key)
119         if isinstance(data, bool):
120             data = "1" if data else "0"
121         else:
122             data = str(data)
123 
124         el.text = data
125 
126     return fits
127 
128 
129 def generate_jp2_xmlbox(header):
130     """
131     Generates the JP2 XML box to be inserted into the jp2 file.
132 
133     Parameters
134     ----------
135     header : `MetaDict`
136         A header dictionary.
137 
138     Returns
139     ----------
140     `XMLBox`
141         XML box containing FITS metadata to be used in jp2 headers
142     """
143     # glymur uses lxml and will crash if trying to use
144     # python's builtin xml.etree
145     import lxml.etree as ET
146     from glymur import jp2box
147 
148     header_xml = header_to_xml(header)
149     meta = ET.Element("meta")
150     meta.append(header_xml)
151     tree = ET.ElementTree(meta)
152     return jp2box.XMLBox(xml=tree)
153 
154 
155 def write(fname, data, header, **kwargs):
156     """
157     Take a data header pair and write a JP2 file.
158 
159     Parameters
160     ----------
161     fname : `str`
162         File name, with extension.
163     data : `numpy.ndarray`
164         n-dimensional data array.
165     header : `dict`
166         A header dictionary.
167     kwargs :
168         Additional keyword args are passed to the glymur.Jp2k constructor
169 
170     Notes
171     -----
172     Saving as a JPEG2000 will cast the data array to
173     uint8 values to support the JPEG2000 format.
174     """
175     from glymur import Jp2k
176 
177     tmpname = fname + "tmp.jp2"
178     jp2_data = np.uint8(data)
179     jp2 = Jp2k(tmpname, jp2_data, **kwargs)
180 
181     # Append the XML data to the header information stored in jp2.box
182     meta_boxes = jp2.box
183     target_index = len(meta_boxes) - 1
184     fits_box = generate_jp2_xmlbox(header)
185     meta_boxes.insert(target_index, fits_box)
186 
187     # Rewrites the jp2 file on disk with the xml data in the header
188     jp2.wrap(fname, boxes=meta_boxes)
189 
190     os.remove(tmpname)
191
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sunpy/io/_jp2.py b/sunpy/io/_jp2.py
--- a/sunpy/io/_jp2.py
+++ b/sunpy/io/_jp2.py
@@ -176,7 +176,11 @@
 
     tmpname = fname + "tmp.jp2"
     jp2_data = np.uint8(data)
-    jp2 = Jp2k(tmpname, jp2_data, **kwargs)
+
+    # The jp2 data is flipped when read in, so we have to flip it back before
+    # saving. See https://github.com/sunpy/sunpy/pull/768 for context.
+    flipped = np.flip(jp2_data, 0)
+    jp2 = Jp2k(tmpname, flipped, **kwargs)
 
     # Append the XML data to the header information stored in jp2.box
     meta_boxes = jp2.box

{"golden_diff": "diff --git a/sunpy/io/_jp2.py b/sunpy/io/_jp2.py\n--- a/sunpy/io/_jp2.py\n+++ b/sunpy/io/_jp2.py\n@@ -176,7 +176,11 @@\n \n     tmpname = fname + \"tmp.jp2\"\n     jp2_data = np.uint8(data)\n-    jp2 = Jp2k(tmpname, jp2_data, **kwargs)\n+\n+    # The jp2 data is flipped when read in, so we have to flip it back before\n+    # saving. See https://github.com/sunpy/sunpy/pull/768 for context.\n+    flipped = np.flip(jp2_data, 0)\n+    jp2 = Jp2k(tmpname, flipped, **kwargs)\n \n     # Append the XML data to the header information stored in jp2.box\n     meta_boxes = jp2.box\n", "issue": "Saving with the \".jp2\" extension results in a vertically flipped image\n### Describe the bug\n\nImages get flipped when saving as jp2 files.\r\nThis was my fault when I added this feature \ud83c\udf89\n\n### To Reproduce\n\n```\r\nfrom sunpy.map import Map\r\nm = Map(\"https://helioviewer.org/jp2/AIA/2024/03/08/304/2024_03_08__00_00_05_123__SDO_AIA_AIA_304.jp2\")\r\nm.peek()\r\nm.save(\"test.jp2\")\r\nflipped = Map(\"test.jp2\")\r\nflipped.peek()\r\n```\r\n\r\nSee screenshots below\r\n\n\n### Screenshots\n\n`m.peek()`\r\n<img width=\"634\" alt=\"image\" src=\"https://github.com/sunpy/sunpy/assets/94071409/fc6f1648-9bd3-4e11-a726-9b2d4810e42e\">\r\n\r\n`flipped.peek()`\r\n<img width=\"634\" alt=\"image\" src=\"https://github.com/sunpy/sunpy/assets/94071409/04c66615-05b3-4776-81c6-7aefa3c9ca5f\">\r\n\n\n### System Details\n\n\r\n>>> import sunpy\r\n>>> sunpy.util.system_info()\r\n==============================\r\nsunpy Installation Information\r\n==============================\r\n\r\nGeneral\r\n#######\r\nOS: Mac OS 14.3.1\r\nArch: 64bit, (arm)\r\nsunpy: 4.0.dev2573+gf79e5d92d\r\nInstallation path: sunpy.egg-info\r\n\r\nRequired Dependencies\r\n#####################\r\nastropy: 6.0.0\r\nnumpy: 1.26.4\r\npackaging: 23.2\r\nparfive: 2.0.2\r\npyerfa: 2.0.1.1\r\nrequests: 2.31.0\r\n\r\nOptional Dependencies\r\n#####################\r\nsunpy: 4.0.dev2573+gf79e5d92d\n\n### Installation method\n\ngit checkout\n", "before_files": [{"content": "\"\"\"\nThis module provides a JPEG 2000 file reader for internal use.\n\n.. warning::\n\n    ``sunpy.io.jp2`` is deprecated, and will be removed in sunpy 4.1. This is\n    because it was designed for internal use only.\n\"\"\"\nimport os\nfrom xml.etree import ElementTree as ET\n\nimport numpy as np\n\nfrom sunpy.io.header import FileHeader\nfrom sunpy.util.io import HDPair, string_is_float\nfrom sunpy.util.xml import xml_to_dict\n\n__all__ = ['read', 'get_header', 'write']\n\n\ndef read(filepath, **kwargs):\n    \"\"\"\n    Reads a JPEG2000 file.\n\n    Parameters\n    ----------\n    filepath : `str`\n        The file to be read.\n    **kwargs : `dict`\n        Unused.\n\n    Returns\n    -------\n    `list`\n        A list of (data, header) tuples.\n    \"\"\"\n    # Put import here to speed up sunpy.io import time\n    from glymur import Jp2k\n\n    header = get_header(filepath)\n    data = Jp2k(filepath)[...][::-1]\n    return [HDPair(data, header[0])]\n\n\ndef get_header(filepath):\n    \"\"\"\n    Reads the header from the file.\n\n    Parameters\n    ----------\n    filepath : `str`\n        The file to be read.\n\n    Returns\n    -------\n    `list`\n        A list of one header read from the file.\n    \"\"\"\n    # Put import here to speed up sunpy.io import time\n    from glymur import Jp2k\n    jp2 = Jp2k(filepath)\n    xml_box = [box for box in jp2.box if box.box_id == 'xml ']\n    xmlstring = ET.tostring(xml_box[0].xml.find('fits'))\n    pydict = xml_to_dict(xmlstring)[\"fits\"]\n\n    # Fix types\n    for k, v in pydict.items():\n        if v.isdigit():\n            pydict[k] = int(v)\n        elif string_is_float(v):\n            pydict[k] = float(v)\n\n    # Remove newlines from comment\n    if 'comment' in pydict:\n        pydict['comment'] = pydict['comment'].replace(\"\\n\", \"\")\n\n    # Is this file a Helioviewer Project JPEG2000 file?\n    pydict['helioviewer'] = xml_box[0].xml.find('helioviewer') is not None\n\n    return [FileHeader(pydict)]\n\n\ndef header_to_xml(header):\n    \"\"\"\n    Converts image header metadata into an XML Tree that can be inserted into\n    a JP2 file header.\n\n    Parameters\n    ----------\n    header : `MetaDict`\n        A header dictionary to convert to xml.\n\n    Returns\n    ----------\n    `lxml.etree._Element`\n        A fits element where each child is an xml element\n        in the form <key>value</key> derived from the key/value\n        pairs in the given header dictionary\n    \"\"\"\n    # glymur uses lxml and will crash if trying to use\n    # python's builtin xml.etree\n    import lxml.etree as ET\n\n    fits = ET.Element(\"fits\")\n\n    already_added = set()\n    for key in header:\n        # Some headers span multiple lines and get duplicated as keys\n        # header.get will appropriately return all data, so if we see\n        # a key again, we can assume it was already added to the xml tree.\n        if (key in already_added):\n            continue\n\n        # Add to the set so we don't duplicate entries\n        already_added.add(key)\n\n        el = ET.SubElement(fits, key)\n        data = header.get(key)\n        if isinstance(data, bool):\n            data = \"1\" if data else \"0\"\n        else:\n            data = str(data)\n\n        el.text = data\n\n    return fits\n\n\ndef generate_jp2_xmlbox(header):\n    \"\"\"\n    Generates the JP2 XML box to be inserted into the jp2 file.\n\n    Parameters\n    ----------\n    header : `MetaDict`\n        A header dictionary.\n\n    Returns\n    ----------\n    `XMLBox`\n        XML box containing FITS metadata to be used in jp2 headers\n    \"\"\"\n    # glymur uses lxml and will crash if trying to use\n    # python's builtin xml.etree\n    import lxml.etree as ET\n    from glymur import jp2box\n\n    header_xml = header_to_xml(header)\n    meta = ET.Element(\"meta\")\n    meta.append(header_xml)\n    tree = ET.ElementTree(meta)\n    return jp2box.XMLBox(xml=tree)\n\n\ndef write(fname, data, header, **kwargs):\n    \"\"\"\n    Take a data header pair and write a JP2 file.\n\n    Parameters\n    ----------\n    fname : `str`\n        File name, with extension.\n    data : `numpy.ndarray`\n        n-dimensional data array.\n    header : `dict`\n        A header dictionary.\n    kwargs :\n        Additional keyword args are passed to the glymur.Jp2k constructor\n\n    Notes\n    -----\n    Saving as a JPEG2000 will cast the data array to\n    uint8 values to support the JPEG2000 format.\n    \"\"\"\n    from glymur import Jp2k\n\n    tmpname = fname + \"tmp.jp2\"\n    jp2_data = np.uint8(data)\n    jp2 = Jp2k(tmpname, jp2_data, **kwargs)\n\n    # Append the XML data to the header information stored in jp2.box\n    meta_boxes = jp2.box\n    target_index = len(meta_boxes) - 1\n    fits_box = generate_jp2_xmlbox(header)\n    meta_boxes.insert(target_index, fits_box)\n\n    # Rewrites the jp2 file on disk with the xml data in the header\n    jp2.wrap(fname, boxes=meta_boxes)\n\n    os.remove(tmpname)\n", "path": "sunpy/io/_jp2.py"}], "after_files": [{"content": "\"\"\"\nThis module provides a JPEG 2000 file reader for internal use.\n\n.. warning::\n\n    ``sunpy.io.jp2`` is deprecated, and will be removed in sunpy 4.1. This is\n    because it was designed for internal use only.\n\"\"\"\nimport os\nfrom xml.etree import ElementTree as ET\n\nimport numpy as np\n\nfrom sunpy.io.header import FileHeader\nfrom sunpy.util.io import HDPair, string_is_float\nfrom sunpy.util.xml import xml_to_dict\n\n__all__ = ['read', 'get_header', 'write']\n\n\ndef read(filepath, **kwargs):\n    \"\"\"\n    Reads a JPEG2000 file.\n\n    Parameters\n    ----------\n    filepath : `str`\n        The file to be read.\n    **kwargs : `dict`\n        Unused.\n\n    Returns\n    -------\n    `list`\n        A list of (data, header) tuples.\n    \"\"\"\n    # Put import here to speed up sunpy.io import time\n    from glymur import Jp2k\n\n    header = get_header(filepath)\n    data = Jp2k(filepath)[...][::-1]\n    return [HDPair(data, header[0])]\n\n\ndef get_header(filepath):\n    \"\"\"\n    Reads the header from the file.\n\n    Parameters\n    ----------\n    filepath : `str`\n        The file to be read.\n\n    Returns\n    -------\n    `list`\n        A list of one header read from the file.\n    \"\"\"\n    # Put import here to speed up sunpy.io import time\n    from glymur import Jp2k\n    jp2 = Jp2k(filepath)\n    xml_box = [box for box in jp2.box if box.box_id == 'xml ']\n    xmlstring = ET.tostring(xml_box[0].xml.find('fits'))\n    pydict = xml_to_dict(xmlstring)[\"fits\"]\n\n    # Fix types\n    for k, v in pydict.items():\n        if v.isdigit():\n            pydict[k] = int(v)\n        elif string_is_float(v):\n            pydict[k] = float(v)\n\n    # Remove newlines from comment\n    if 'comment' in pydict:\n        pydict['comment'] = pydict['comment'].replace(\"\\n\", \"\")\n\n    # Is this file a Helioviewer Project JPEG2000 file?\n    pydict['helioviewer'] = xml_box[0].xml.find('helioviewer') is not None\n\n    return [FileHeader(pydict)]\n\n\ndef header_to_xml(header):\n    \"\"\"\n    Converts image header metadata into an XML Tree that can be inserted into\n    a JP2 file header.\n\n    Parameters\n    ----------\n    header : `MetaDict`\n        A header dictionary to convert to xml.\n\n    Returns\n    ----------\n    `lxml.etree._Element`\n        A fits element where each child is an xml element\n        in the form <key>value</key> derived from the key/value\n        pairs in the given header dictionary\n    \"\"\"\n    # glymur uses lxml and will crash if trying to use\n    # python's builtin xml.etree\n    import lxml.etree as ET\n\n    fits = ET.Element(\"fits\")\n\n    already_added = set()\n    for key in header:\n        # Some headers span multiple lines and get duplicated as keys\n        # header.get will appropriately return all data, so if we see\n        # a key again, we can assume it was already added to the xml tree.\n        if (key in already_added):\n            continue\n\n        # Add to the set so we don't duplicate entries\n        already_added.add(key)\n\n        el = ET.SubElement(fits, key)\n        data = header.get(key)\n        if isinstance(data, bool):\n            data = \"1\" if data else \"0\"\n        else:\n            data = str(data)\n\n        el.text = data\n\n    return fits\n\n\ndef generate_jp2_xmlbox(header):\n    \"\"\"\n    Generates the JP2 XML box to be inserted into the jp2 file.\n\n    Parameters\n    ----------\n    header : `MetaDict`\n        A header dictionary.\n\n    Returns\n    ----------\n    `XMLBox`\n        XML box containing FITS metadata to be used in jp2 headers\n    \"\"\"\n    # glymur uses lxml and will crash if trying to use\n    # python's builtin xml.etree\n    import lxml.etree as ET\n    from glymur import jp2box\n\n    header_xml = header_to_xml(header)\n    meta = ET.Element(\"meta\")\n    meta.append(header_xml)\n    tree = ET.ElementTree(meta)\n    return jp2box.XMLBox(xml=tree)\n\n\ndef write(fname, data, header, **kwargs):\n    \"\"\"\n    Take a data header pair and write a JP2 file.\n\n    Parameters\n    ----------\n    fname : `str`\n        File name, with extension.\n    data : `numpy.ndarray`\n        n-dimensional data array.\n    header : `dict`\n        A header dictionary.\n    kwargs :\n        Additional keyword args are passed to the glymur.Jp2k constructor\n\n    Notes\n    -----\n    Saving as a JPEG2000 will cast the data array to\n    uint8 values to support the JPEG2000 format.\n    \"\"\"\n    from glymur import Jp2k\n\n    tmpname = fname + \"tmp.jp2\"\n    jp2_data = np.uint8(data)\n\n    # The jp2 data is flipped when read in, so we have to flip it back before\n    # saving. See https://github.com/sunpy/sunpy/pull/768 for context.\n    flipped = np.flip(jp2_data, 0)\n    jp2 = Jp2k(tmpname, flipped, **kwargs)\n\n    # Append the XML data to the header information stored in jp2.box\n    meta_boxes = jp2.box\n    target_index = len(meta_boxes) - 1\n    fits_box = generate_jp2_xmlbox(header)\n    meta_boxes.insert(target_index, fits_box)\n\n    # Rewrites the jp2 file on disk with the xml data in the header\n    jp2.wrap(fname, boxes=meta_boxes)\n\n    os.remove(tmpname)\n", "path": "sunpy/io/_jp2.py"}]}