diff --git a/README.md b/README.md index de16aa6..b08ed8b 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,8 @@ my_char_rotation = my_char_ctm.skew_x | Property | Description | |----------|-------------| |`page_number`| Page number on which this curve was found.| -|`pts`| Points — as a list of `(x, top)` tuples — describing the curve.| +|`pts`| A list of `(x, top)` tuples indicating the *points on the curve*.| +|`path`| A list of `(cmd, *(x, top))` tuples *describing the full path description*, including (for example) control points used in Bezier curves.| |`height`| Height of curve's bounding box.| |`width`| Width of curve's bounding box.| |`x0`| Distance of curve's left-most point from left side of page.| @@ -243,6 +244,7 @@ my_char_rotation = my_char_ctm.skew_x |`fill`| Whether the shape defined by the curve's path is filled.| |`stroking_color`|The color of the curve's outline. See [docs/colors.md](docs/colors.md) for details.| |`non_stroking_color`|The curve’s fill color. See [docs/colors.md](docs/colors.md) for details.| +|`dash`|A `([dash_array], dash_phase)` tuple describing the curve's dash style. See [Table 4.6 of the PDF specification](https://ghostscript.com/~robin/pdf_reference17.pdf#page=218) for details.| |`mcid`| The [marked content](https://ghostscript.com/~robin/pdf_reference17.pdf#page=850) section ID for this curve if any (otherwise `None`). *Experimental attribute.*| |`tag`| The [marked content](https://ghostscript.com/~robin/pdf_reference17.pdf#page=850) section tag for this curve if any (otherwise `None`). *Experimental attribute.*| |`object_type`| "curve"| diff --git a/pdfplumber/page.py b/pdfplumber/page.py index 75437af..4a685a1 100644 --- a/pdfplumber/page.py +++ b/pdfplumber/page.py @@ -18,6 +18,7 @@ from pdfminer.layout import ( LTChar, LTComponent, LTContainer, + LTCurve, LTItem, LTPage, LTTextContainer, @@ -59,10 +60,9 @@ ALL_ATTRS = set( "evenodd", "fill", "non_stroking_color", - "path", - "stream", "stroke", "stroking_color", + "stream", "mcid", "tag", ] @@ -383,9 +383,15 @@ class Page(Container): if isinstance(attr["fontname"], bytes): attr["fontname"] = fix_fontname_bytes(attr["fontname"]) - if "pts" in attr: + elif isinstance(obj, (LTCurve,)): attr["pts"] = list(map(self.point2coord, attr["pts"])) + # Ignoring typing because type signature for obj.original_path + # appears to be incorrect + attr["path"] = [(cmd, *map(self.point2coord, pts)) for cmd, *pts in obj.original_path] # type: ignore # noqa: E501 + + attr["dash"] = obj.dashing_style + if "y0" in attr: attr["top"] = self.height - attr["y1"] attr["bottom"] = self.height - attr["y0"] diff --git a/tests/test_convert.py b/tests/test_convert.py index fd488c5..78731bf 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -177,8 +177,8 @@ class Test(unittest.TestCase): c = self.pdf.to_csv(precision=3) assert c.split("\r\n")[9] == ( "char,1,45.83,58.826,656.82,674.82,117.18,117.18,135.18,12.996," - '18.0,12.996,,,,,,TimesNewRomanPSMT,,,"(1, 0, 0, 1, 45.83, 660.69)"' - ',,DeviceRGB,"(0, 0, 0)",,,18.0,,,,,,,Y,,1,' + '18.0,12.996,,,,,,,TimesNewRomanPSMT,,,"(1, 0, 0, 1, 45.83, 660.69)"' + ',,DeviceRGB,"(0, 0, 0)",,,,18.0,,,,,,,Y,,1,' ) io = StringIO() @@ -244,8 +244,8 @@ class Test(unittest.TestCase): assert res.decode("utf-8").split("\r\n")[9] == ( "char,1,45.83,58.826,656.82,674.82,117.18,117.18,135.18,12.996," - '18.0,12.996,,,,,,TimesNewRomanPSMT,,,"(1, 0, 0, 1, 45.83, 660.69)"' - ',,DeviceRGB,"(0, 0, 0)",,,18.0,,,,,,,Y,,1,' + '18.0,12.996,,,,,,,TimesNewRomanPSMT,,,"(1, 0, 0, 1, 45.83, 660.69)"' + ',,DeviceRGB,"(0, 0, 0)",,,,18.0,,,,,,,Y,,1,' ) def test_cli_csv_exclude(self): @@ -270,8 +270,8 @@ class Test(unittest.TestCase): assert res.decode("utf-8").split("\r\n")[9] == ( "char,1,45.83,58.826,656.82,674.82,117.18,117.18,135.18,12.996," - "18.0,12.996,,,,,,TimesNewRomanPSMT," - ',,"(0, 0, 0)",,18.0,,,,,,Y,,1,' + "18.0,12.996,,,,,,,TimesNewRomanPSMT," + ',,"(0, 0, 0)",,,18.0,,,,,,Y,,1,' ) def test_cli_csv_include(self):