diff --git a/CHANGELOG.md b/CHANGELOG.md index 9890ce6c0..d55e49427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,10 @@ follow semantic versioning; release dates are ISO 8601. the header row, and moved the whole page under the header down that much. That reach now comes out of the cell's own space under it first, and what passes the row comes out of the gap under the row. `TimelineMinimal`'s median drift falls from 1.44 to 0.36pt in Word and from 1.40 to 0.45pt in - LibreOffice, its cover letter's from 1.30 to 0.29pt and 1.32 to 0.33pt. + LibreOffice, its cover letter's from 1.30 to 0.29pt and 1.32 to 0.33pt. A spacer the layout + moves to the next page leaves such a reach — and a pull or a card's border below the block + before it — on the page above, and holds the whole gap carried with it, as a paragraph moved + there does. - **A spacer is as tall in DOCX as on the page.** A spacer is written as an empty paragraph a tenth of a point tall, which Word keeps (measured: ten such lines between eleven lines of text added 0.96pt), and its whole height was then owed below it as well. Every spacer stood a diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv index 698a35600..4e7132de8 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv @@ -1633,74 +1633,74 @@ cv-timeline_minimal 0:89674e6f130e -0.19 +442055551000 cv-timeline_minimal 0:e0f850305ded -0.19 jordan.rivera@example.com cv-timeline_minimal 0:84308bea4540 -0.19 linkedin cv-timeline_minimal 0:c0b0109d9439 -0.19 github -cv-timeline_minimal 0:68bf0109c6f8 1.90 education -cv-timeline_minimal 0:54654b90a771 1.40 msccomputerscience|universityof -cv-timeline_minimal 0:a57111bb5a18 1.40 manchester-2019-2021 -cv-timeline_minimal 0:bf13436325bd 1.40 distinction.thesis:composablelay -cv-timeline_minimal 0:4c7268c098b3 1.40 primitivesfordeterministicdocume -cv-timeline_minimal 0:6154cc9024ae 1.40 rendering. -cv-timeline_minimal 0:17dd75fc575f 1.40 bscsoftwareengineering|imperialc -cv-timeline_minimal 0:220e665a6446 1.40 london-2015-2019 -cv-timeline_minimal 0:048a150be6e7 1.40 first-classhonours.specialisatio -cv-timeline_minimal 0:f9d33eb2a1a2 1.40 compilersandstaticanalysis. -cv-timeline_minimal 0:dddc36d8bb44 1.40 oraclejavacertification|professi -cv-timeline_minimal 0:6c065bee3945 1.40 track-2023-2024 -cv-timeline_minimal 0:40f2bfbae245 1.40 java17platformdeep-dive:records, -cv-timeline_minimal 0:6cba964fe5fe 1.40 types,patternmatching,virtualthr -cv-timeline_minimal 0:5f94c50dc5bf 1.90 technicalskills -cv-timeline_minimal 0:5d6819554c7a 1.40 languages:java21,kotlin,groovy,p -cv-timeline_minimal 0:4ec7c53222c8 1.40 sql -cv-timeline_minimal 0:db84150190bb 1.40 document&print:pdfbox,apachepoi -cv-timeline_minimal 0:69d03fef7130 1.40 (docx/xlsx),itext,postscript,icc -cv-timeline_minimal 0:689d68053b81 1.40 profiles,fontmetrics -cv-timeline_minimal 0:8ca175286c7b 1.40 layoutengines:customdsldesign, -cv-timeline_minimal 0:b6f2e7d194af 1.40 semanticlayouttrees,pagination,s -cv-timeline_minimal 0:e8951d583db6 1.40 testing,visualregression -cv-timeline_minimal 0:3b7a3ab3b6ee 1.40 build&infrastructure:maven,gradl -cv-timeline_minimal 0:d176e972b547 1.40 githubactions,jitpack,docker,jmh -cv-timeline_minimal 0:3d119f794292 1.40 benchmarking -cv-timeline_minimal 0:b7b6a102880a 1.40 testing:junit5,assertj,pdfbox-ba -cv-timeline_minimal 0:2f0dced56e7f 1.40 pngdiff,layout-graphsnapshots,mu -cv-timeline_minimal 0:c001aa5cc6fb 1.40 testing(pitest) -cv-timeline_minimal 0:90c057c30e8f 1.40 distribution:mavencentral,sonaty -cv-timeline_minimal 0:7347a19cb48d 1.40 ossrh,gpgsigning,jitpack,semanti -cv-timeline_minimal 0:b13180079f20 1.40 versioningdiscipline -cv-timeline_minimal 0:2577c0f557b2 1.90 projects -cv-timeline_minimal 0:bd82e94c4024 1.40 graphcompose(java21,pdfbox,maven -cv-timeline_minimal 0:54b7bb3a12a0 1.40 jmh):declarativejavapdflayouteng -cv-timeline_minimal 0:83ee0a75c2d4 1.40 semanticdsl,slot-basedtemplates, -cv-timeline_minimal 0:235802baf524 1.40 snapshottesting.powersproduction -cv-timeline_minimal 0:44925c9e42ee 1.40 invoice/proposalpipelinesforhiri -cv-timeline_minimal 0:ae7902eee4b2 1.40 templatestudio(kotlin,composedes -cv-timeline_minimal 0:f15436034237 1.40 pdfboxpngdiff):internaltoolfor -cv-timeline_minimal 0:24a195694090 1.40 evaluatingcv,proposal,andinvoice -cv-timeline_minimal 0:dbcc5c321887 1.40 layoutlint(java21,javaparser,spo -cv-timeline_minimal 0:a1446929a276 1.40 staticanalyserthatflagsfragileau -cv-timeline_minimal 0:d91205080ec3 1.40 patterns(deeplynestedrows,untype -cv-timeline_minimal 0:d31cf3fc9dae 1.40 offsets,implicitpagebreaks)befor -cv-timeline_minimal 0:7b985a1d2b08 1.40 shiptoproduction. -cv-timeline_minimal 0:14e902e0e2ff 1.40 chromeforge(java,graphcompose, -cv-timeline_minimal 0:f87a435df3ac 1.40 pandocbridge):editorial-magazine -cv-timeline_minimal 0:73678fb1255e 1.40 documenttoolkitbuiltongraphcompo -cv-timeline_minimal 0:a0c280e181e2 1.40 cinematiccovers,pullquotes,multi -cv-timeline_minimal 0:d9f1b263312f 1.40 flow,sidebarcallouts. -cv-timeline_minimal 0:b944906ed84c 1.85 professionalsummary -cv-timeline_minimal 0:4bc9e0d2344b 1.30 platformengineerwith10+yearsbuil -cv-timeline_minimal 0:453eef50417c 1.32 anddeveloper-facingtemplatesyste -cv-timeline_minimal 0:39881b36a9c0 1.34 authoringdsls,andturningbrittlep -cv-timeline_minimal 0:83064bdc95f5 1.36 scale. -cv-timeline_minimal 0:c2f296ef30a3 1.93 professionalexperience -cv-timeline_minimal 0:68c08b2a5966 1.63 seniorplatformengineer|northwind -cv-timeline_minimal 0:64c4b52dceb1 1.37 ledthereusabledocument-generatio -cv-timeline_minimal 0:1f193cd1d987 1.36 productteams.reducedtemplatemain -cv-timeline_minimal 0:9ccc3e904e5a 1.35 favourofonecanonicalengine. -cv-timeline_minimal 0:fb53bd1bfeb9 1.59 softwareengineer|brightleaflabs- -cv-timeline_minimal 0:632618b20dab 1.33 builtbackendservicesandproductio -cv-timeline_minimal 0:5d86afffe129 1.32 month.drovethemigrationfromitext -cv-timeline_minimal 0:70dd947a4558 1.31 cuttingp99renderlatencyfrom1.4st -cv-timeline_minimal 0:8a9898d2c965 1.55 backendengineer|helixprintco-201 -cv-timeline_minimal 0:a485c37fe7ea 1.29 maintainedahigh-volumeinvoice-pr -cv-timeline_minimal 0:5a136b6b9d8c 1.28 testharnessthatgatedeverytemplat +cv-timeline_minimal 0:68bf0109c6f8 0.05 education +cv-timeline_minimal 0:54654b90a771 -0.45 msccomputerscience|universityof +cv-timeline_minimal 0:a57111bb5a18 -0.45 manchester-2019-2021 +cv-timeline_minimal 0:bf13436325bd -0.45 distinction.thesis:composablelay +cv-timeline_minimal 0:4c7268c098b3 -0.45 primitivesfordeterministicdocume +cv-timeline_minimal 0:6154cc9024ae -0.45 rendering. +cv-timeline_minimal 0:17dd75fc575f -0.45 bscsoftwareengineering|imperialc +cv-timeline_minimal 0:220e665a6446 -0.45 london-2015-2019 +cv-timeline_minimal 0:048a150be6e7 -0.45 first-classhonours.specialisatio +cv-timeline_minimal 0:f9d33eb2a1a2 -0.45 compilersandstaticanalysis. +cv-timeline_minimal 0:dddc36d8bb44 -0.45 oraclejavacertification|professi +cv-timeline_minimal 0:6c065bee3945 -0.45 track-2023-2024 +cv-timeline_minimal 0:40f2bfbae245 -0.45 java17platformdeep-dive:records, +cv-timeline_minimal 0:6cba964fe5fe -0.45 types,patternmatching,virtualthr +cv-timeline_minimal 0:5f94c50dc5bf 0.05 technicalskills +cv-timeline_minimal 0:5d6819554c7a -0.45 languages:java21,kotlin,groovy,p +cv-timeline_minimal 0:4ec7c53222c8 -0.45 sql +cv-timeline_minimal 0:db84150190bb -0.45 document&print:pdfbox,apachepoi +cv-timeline_minimal 0:69d03fef7130 -0.45 (docx/xlsx),itext,postscript,icc +cv-timeline_minimal 0:689d68053b81 -0.45 profiles,fontmetrics +cv-timeline_minimal 0:8ca175286c7b -0.45 layoutengines:customdsldesign, +cv-timeline_minimal 0:b6f2e7d194af -0.45 semanticlayouttrees,pagination,s +cv-timeline_minimal 0:e8951d583db6 -0.45 testing,visualregression +cv-timeline_minimal 0:3b7a3ab3b6ee -0.45 build&infrastructure:maven,gradl +cv-timeline_minimal 0:d176e972b547 -0.45 githubactions,jitpack,docker,jmh +cv-timeline_minimal 0:3d119f794292 -0.45 benchmarking +cv-timeline_minimal 0:b7b6a102880a -0.45 testing:junit5,assertj,pdfbox-ba +cv-timeline_minimal 0:2f0dced56e7f -0.45 pngdiff,layout-graphsnapshots,mu +cv-timeline_minimal 0:c001aa5cc6fb -0.45 testing(pitest) +cv-timeline_minimal 0:90c057c30e8f -0.45 distribution:mavencentral,sonaty +cv-timeline_minimal 0:7347a19cb48d -0.45 ossrh,gpgsigning,jitpack,semanti +cv-timeline_minimal 0:b13180079f20 -0.45 versioningdiscipline +cv-timeline_minimal 0:2577c0f557b2 0.05 projects +cv-timeline_minimal 0:bd82e94c4024 -0.45 graphcompose(java21,pdfbox,maven +cv-timeline_minimal 0:54b7bb3a12a0 -0.45 jmh):declarativejavapdflayouteng +cv-timeline_minimal 0:83ee0a75c2d4 -0.45 semanticdsl,slot-basedtemplates, +cv-timeline_minimal 0:235802baf524 -0.45 snapshottesting.powersproduction +cv-timeline_minimal 0:44925c9e42ee -0.45 invoice/proposalpipelinesforhiri +cv-timeline_minimal 0:ae7902eee4b2 -0.45 templatestudio(kotlin,composedes +cv-timeline_minimal 0:f15436034237 -0.45 pdfboxpngdiff):internaltoolfor +cv-timeline_minimal 0:24a195694090 -0.45 evaluatingcv,proposal,andinvoice +cv-timeline_minimal 0:dbcc5c321887 -0.45 layoutlint(java21,javaparser,spo +cv-timeline_minimal 0:a1446929a276 -0.45 staticanalyserthatflagsfragileau +cv-timeline_minimal 0:d91205080ec3 -0.45 patterns(deeplynestedrows,untype +cv-timeline_minimal 0:d31cf3fc9dae -0.45 offsets,implicitpagebreaks)befor +cv-timeline_minimal 0:7b985a1d2b08 -0.45 shiptoproduction. +cv-timeline_minimal 0:14e902e0e2ff -0.45 chromeforge(java,graphcompose, +cv-timeline_minimal 0:f87a435df3ac -0.45 pandocbridge):editorial-magazine +cv-timeline_minimal 0:73678fb1255e -0.45 documenttoolkitbuiltongraphcompo +cv-timeline_minimal 0:a0c280e181e2 -0.45 cinematiccovers,pullquotes,multi +cv-timeline_minimal 0:d9f1b263312f -0.45 flow,sidebarcallouts. +cv-timeline_minimal 0:b944906ed84c 0.00 professionalsummary +cv-timeline_minimal 0:4bc9e0d2344b -0.55 platformengineerwith10+yearsbuil +cv-timeline_minimal 0:453eef50417c -0.53 anddeveloper-facingtemplatesyste +cv-timeline_minimal 0:39881b36a9c0 -0.51 authoringdsls,andturningbrittlep +cv-timeline_minimal 0:83064bdc95f5 -0.49 scale. +cv-timeline_minimal 0:c2f296ef30a3 0.08 professionalexperience +cv-timeline_minimal 0:68c08b2a5966 -0.22 seniorplatformengineer|northwind +cv-timeline_minimal 0:64c4b52dceb1 -0.48 ledthereusabledocument-generatio +cv-timeline_minimal 0:1f193cd1d987 -0.49 productteams.reducedtemplatemain +cv-timeline_minimal 0:9ccc3e904e5a -0.50 favourofonecanonicalengine. +cv-timeline_minimal 0:fb53bd1bfeb9 -0.26 softwareengineer|brightleaflabs- +cv-timeline_minimal 0:632618b20dab -0.52 builtbackendservicesandproductio +cv-timeline_minimal 0:5d86afffe129 -0.53 month.drovethemigrationfromitext +cv-timeline_minimal 0:70dd947a4558 -0.54 cuttingp99renderlatencyfrom1.4st +cv-timeline_minimal 0:8a9898d2c965 -0.30 backendengineer|helixprintco-201 +cv-timeline_minimal 0:a485c37fe7ea -0.56 maintainedahigh-volumeinvoice-pr +cv-timeline_minimal 0:5a136b6b9d8c -0.57 testharnessthatgatedeverytemplat cv-timeline_minimal 1:676dfdd56ad2 0.20 additionalinformation cv-timeline_minimal 1:ecc25e73b19b -0.30 languages:english(fluent),german cv-timeline_minimal 1:ac7f0ee6a49e -0.30 (intermediate),spanish(basic) @@ -1719,75 +1719,75 @@ cv-violet_grid 0:77b27b9ce612 -0.11 +1(415)555-7842 cv-violet_grid 0:12d25ebdca93 -0.11 sanfrancisco,ca,usa cv-violet_grid 0:8a2bdbd153f4 -0.11 sofiamartinez.design cv-violet_grid 0:84308bea4540 -0.12 linkedin -cv-violet_grid 0:30b943b69458 0.17 user-centereddesignerwith5+years -cv-violet_grid 0:c5d00ee335d1 0.16 icombineresearch,designthinking, -cv-violet_grid 0:62fd7c29c3c0 0.14 passionateaboutcollaboratingwith -cv-violet_grid 0:32c1cf49a2fe 0.14 skills -cv-violet_grid 0:b96067918254 0.24 uxresearch -cv-violet_grid 0:9058674fd905 0.29 userinterviews,surveys, -cv-violet_grid 0:0cdd38014d79 0.28 personas,journeymaps, -cv-violet_grid 0:61e4ba28d469 0.28 competitiveanalysis -cv-violet_grid 0:db5c6756c4b6 8.09 sitemaps,userflows,card -cv-violet_grid 0:6c7ad1834a60 8.08 sorting,contentstrategy -cv-violet_grid 0:187737d59250 0.24 wireframing -cv-violet_grid 0:2286e5aa328a 0.29 lowtohigh-fidelity -cv-violet_grid 0:5c4b21cb1af8 0.24 prototyping -cv-violet_grid 0:1abfa9dc1773 0.29 interactiveprototypes, -cv-violet_grid 0:1ddc6c379a54 0.28 microinteractions, -cv-violet_grid 0:d1d99c54cf40 0.28 transitions -cv-violet_grid 0:7a38f8d94009 0.24 usabilitytesting -cv-violet_grid 0:63af69ae99e6 0.29 testplanning,usability -cv-violet_grid 0:f67c409cc44f 0.28 tests,heuristicevaluation, -cv-violet_grid 0:438e1d3264dd 0.28 insights&iteration -cv-violet_grid 0:7e02941d76f5 0.24 designsystems -cv-violet_grid 0:a0d27161913a 0.29 componentlibraries, -cv-violet_grid 0:82374d275507 0.28 designtokens,pattern -cv-violet_grid 0:caf9f906b16a 0.28 documentation -cv-violet_grid 0:f9d35d43770d 0.26 tools -cv-violet_grid 0:5cb2b3b6143c 0.32 figmafigjamadobexdsketchmironoti -cv-violet_grid 0:53e5e7c5a884 0.30 experience -cv-violet_grid 0:5a0774260e0c 0.42 2022-present -cv-violet_grid 0:204f460767ff 0.38 seniorux/uidesigner|novafin(fint -cv-violet_grid 0:318bba211bdc 0.43 sanfrancisco,ca -cv-violet_grid 0:318bba211bdc#1 9.35 sanfrancisco,ca -cv-violet_grid 0:3883e88c63c3 0.42 ledend-to-enddesignforapersonalf -cv-violet_grid 0:ccc00663f089 2.20 conducteduserresearch,builtperso -cv-violet_grid 0:7d0f908c7e5f 3.98 designedwireframes,prototypes,an -cv-violet_grid 0:9b57c4a85aad 5.77 establishedandmaintainedascalabl -cv-violet_grid 0:6894eafb99c2 7.55 collaboratedwithproductmanagers, -cv-violet_grid 0:01e4455cec51 9.34 2020-2022 -cv-violet_grid 0:24d722f0cfbe 9.30 uxdesigner|brighthealth(healthte -cv-violet_grid 0:530707fb0897 9.34 redesignedpatientportalexperienc -cv-violet_grid 0:b2e27a1d9270 11.12 plannedandfacilitatedusabilityte -cv-violet_grid 0:ab80a52d70fe 12.90 partneredwithengineerstoensurefe -cv-violet_grid 0:11cbf615189b 14.69 createdreusablecomponentsandstyl -cv-violet_grid 0:7e5081a3ed48 16.48 2018-2020 -cv-violet_grid 0:73f7bbf7cd60 16.44 juniorux/uidesigner|lumendigital -cv-violet_grid 0:c6b06045332f 16.49 austin,tx -cv-violet_grid 0:5ea8234925c5 16.48 supporteddiscoveryworkshops,user -cv-violet_grid 0:46f00c76528c 18.26 designedresponsivewebsitesandlan -cv-violet_grid 0:f3cea36a9d83 20.04 collaboratedwithcopywritersandde -cv-violet_grid 0:d94eccbb1fbe 21.81 selectedprojects -cv-violet_grid 0:45dc8bf81211 21.76 novafinmobileapp|personalfinance -cv-violet_grid 0:d398b29d3dbb 21.81 2023 -cv-violet_grid 0:e66c1ce573d3 21.67 ledux/uidesignforbudgeting,goalt -cv-violet_grid 0:0ed8829e68d0 21.67 thatinformedasimplifiednavigatio -cv-violet_grid 0:ee9bd17ebccf 21.67 22%increaseinweeklyactiveusers. -cv-violet_grid 0:28b4603d784a 21.75 brighthealthpatientportal|health -cv-violet_grid 0:1bea20e1df19 21.80 2021 -cv-violet_grid 0:999ddb26bed6 21.66 redesignedkeypatientflowsincludi -cv-violet_grid 0:073d15633c0b 21.66 systemandcomponentlibraryusedacr -cv-violet_grid 0:68bf0109c6f8 21.91 education -cv-violet_grid 0:6cb574fa10b6 21.91 languages -cv-violet_grid 0:0cf7506cf94a 19.58 bacheloroffineartsingraphicdesig -cv-violet_grid 0:00ab7a6aa5f1 19.59 californiacollegeofthearts -cv-violet_grid 0:7e2c21bb41db 19.58 sanfrancisco,ca|2014-2018 -cv-violet_grid 0:b6234d2ea0d6 21.23 english -cv-violet_grid 0:bef32d2c315a 37.97 native -cv-violet_grid 0:bef32d2c315a#1 5.71 native -cv-violet_grid 0:9c9b13446cd9 22.46 spanish -cv-violet_grid 0:e35242c809c8 23.69 portuguese -cv-violet_grid 0:c5d1de4964d6 23.69 conversational +cv-violet_grid 0:30b943b69458 -0.33 user-centereddesignerwith5+years +cv-violet_grid 0:c5d00ee335d1 -0.34 icombineresearch,designthinking, +cv-violet_grid 0:62fd7c29c3c0 -0.36 passionateaboutcollaboratingwith +cv-violet_grid 0:32c1cf49a2fe -0.36 skills +cv-violet_grid 0:b96067918254 -0.26 uxresearch +cv-violet_grid 0:9058674fd905 -0.21 userinterviews,surveys, +cv-violet_grid 0:0cdd38014d79 -0.22 personas,journeymaps, +cv-violet_grid 0:61e4ba28d469 -0.22 competitiveanalysis +cv-violet_grid 0:db5c6756c4b6 7.59 sitemaps,userflows,card +cv-violet_grid 0:6c7ad1834a60 7.58 sorting,contentstrategy +cv-violet_grid 0:187737d59250 -0.26 wireframing +cv-violet_grid 0:2286e5aa328a -0.21 lowtohigh-fidelity +cv-violet_grid 0:5c4b21cb1af8 -0.26 prototyping +cv-violet_grid 0:1abfa9dc1773 -0.21 interactiveprototypes, +cv-violet_grid 0:1ddc6c379a54 -0.22 microinteractions, +cv-violet_grid 0:d1d99c54cf40 -0.22 transitions +cv-violet_grid 0:7a38f8d94009 -0.26 usabilitytesting +cv-violet_grid 0:63af69ae99e6 -0.21 testplanning,usability +cv-violet_grid 0:f67c409cc44f -0.22 tests,heuristicevaluation, +cv-violet_grid 0:438e1d3264dd -0.22 insights&iteration +cv-violet_grid 0:7e02941d76f5 -0.26 designsystems +cv-violet_grid 0:a0d27161913a -0.21 componentlibraries, +cv-violet_grid 0:82374d275507 -0.22 designtokens,pattern +cv-violet_grid 0:caf9f906b16a -0.22 documentation +cv-violet_grid 0:f9d35d43770d -0.24 tools +cv-violet_grid 0:5cb2b3b6143c -0.18 figmafigjamadobexdsketchmironoti +cv-violet_grid 0:53e5e7c5a884 -0.20 experience +cv-violet_grid 0:5a0774260e0c -0.08 2022-present +cv-violet_grid 0:204f460767ff -0.12 seniorux/uidesigner|novafin(fint +cv-violet_grid 0:318bba211bdc -0.07 sanfrancisco,ca +cv-violet_grid 0:318bba211bdc#1 8.85 sanfrancisco,ca +cv-violet_grid 0:3883e88c63c3 -0.08 ledend-to-enddesignforapersonalf +cv-violet_grid 0:ccc00663f089 1.70 conducteduserresearch,builtperso +cv-violet_grid 0:7d0f908c7e5f 3.48 designedwireframes,prototypes,an +cv-violet_grid 0:9b57c4a85aad 5.27 establishedandmaintainedascalabl +cv-violet_grid 0:6894eafb99c2 7.05 collaboratedwithproductmanagers, +cv-violet_grid 0:01e4455cec51 8.84 2020-2022 +cv-violet_grid 0:24d722f0cfbe 8.80 uxdesigner|brighthealth(healthte +cv-violet_grid 0:530707fb0897 8.84 redesignedpatientportalexperienc +cv-violet_grid 0:b2e27a1d9270 10.62 plannedandfacilitatedusabilityte +cv-violet_grid 0:ab80a52d70fe 12.40 partneredwithengineerstoensurefe +cv-violet_grid 0:11cbf615189b 14.19 createdreusablecomponentsandstyl +cv-violet_grid 0:7e5081a3ed48 15.98 2018-2020 +cv-violet_grid 0:73f7bbf7cd60 15.94 juniorux/uidesigner|lumendigital +cv-violet_grid 0:c6b06045332f 15.99 austin,tx +cv-violet_grid 0:5ea8234925c5 15.98 supporteddiscoveryworkshops,user +cv-violet_grid 0:46f00c76528c 17.76 designedresponsivewebsitesandlan +cv-violet_grid 0:f3cea36a9d83 19.54 collaboratedwithcopywritersandde +cv-violet_grid 0:d94eccbb1fbe 21.31 selectedprojects +cv-violet_grid 0:45dc8bf81211 21.26 novafinmobileapp|personalfinance +cv-violet_grid 0:d398b29d3dbb 21.31 2023 +cv-violet_grid 0:e66c1ce573d3 21.17 ledux/uidesignforbudgeting,goalt +cv-violet_grid 0:0ed8829e68d0 21.17 thatinformedasimplifiednavigatio +cv-violet_grid 0:ee9bd17ebccf 21.17 22%increaseinweeklyactiveusers. +cv-violet_grid 0:28b4603d784a 21.25 brighthealthpatientportal|health +cv-violet_grid 0:1bea20e1df19 21.30 2021 +cv-violet_grid 0:999ddb26bed6 21.16 redesignedkeypatientflowsincludi +cv-violet_grid 0:073d15633c0b 21.16 systemandcomponentlibraryusedacr +cv-violet_grid 0:68bf0109c6f8 21.41 education +cv-violet_grid 0:6cb574fa10b6 21.41 languages +cv-violet_grid 0:0cf7506cf94a 19.08 bacheloroffineartsingraphicdesig +cv-violet_grid 0:00ab7a6aa5f1 19.09 californiacollegeofthearts +cv-violet_grid 0:7e2c21bb41db 19.08 sanfrancisco,ca|2014-2018 +cv-violet_grid 0:b6234d2ea0d6 20.73 english +cv-violet_grid 0:bef32d2c315a 37.47 native +cv-violet_grid 0:bef32d2c315a#1 5.21 native +cv-violet_grid 0:9c9b13446cd9 21.96 spanish +cv-violet_grid 0:e35242c809c8 23.19 portuguese +cv-violet_grid 0:c5d1de4964d6 23.19 conversational invoice-classic 0:c26c22b94338 0.49 graphcomposestudio invoice-classic 0:c26c22b94338#1 0.25 graphcomposestudio invoice-classic 0:2a08ce5567fb 0.35 18layoutstreet @@ -3666,20 +3666,20 @@ letter-sidebar_portrait 0:7c5a9817c868 -0.43 iwouldwelcometheopportunitytobri letter-sidebar_portrait 0:4012eb4b05db -0.41 group. letter-sidebar_portrait 0:80a6a628ad52 -0.14 sincerely, letter-timeline_minimal 0:8f1010120ed6 0.15 jordanrivera -letter-timeline_minimal 0:8f1010120ed6#1 1.52 jordanrivera +letter-timeline_minimal 0:8f1010120ed6#1 -0.33 jordanrivera letter-timeline_minimal 0:2d58f0cadc2d -0.40 platformengineer letter-timeline_minimal 0:fbeea547d99e -0.19 london,uk letter-timeline_minimal 0:89674e6f130e -0.19 +442055551000 letter-timeline_minimal 0:e0f850305ded -0.19 jordan.rivera@example.com letter-timeline_minimal 0:84308bea4540 -0.19 linkedin letter-timeline_minimal 0:c0b0109d9439 -0.19 github -letter-timeline_minimal 0:bef81b541365 1.52 dearhiringteamatnorthwindsystems -letter-timeline_minimal 0:ab54410ead82 1.32 iamexcitedtosharemyinterestinthe -letter-timeline_minimal 0:9185168d2cf8 1.32 document-generationsystemsthatba -letter-timeline_minimal 0:a463bced3db2 1.32 ienjoytranslatingfuzzyworkflowre -letter-timeline_minimal 0:dd474e249222 1.32 makeadoptioneasierfortherestofth -letter-timeline_minimal 0:a71e98216baa 1.52 iwouldwelcometheopportunitytobri -letter-timeline_minimal 0:80a6a628ad52 1.52 sincerely, +letter-timeline_minimal 0:bef81b541365 -0.33 dearhiringteamatnorthwindsystems +letter-timeline_minimal 0:ab54410ead82 -0.53 iamexcitedtosharemyinterestinthe +letter-timeline_minimal 0:9185168d2cf8 -0.53 document-generationsystemsthatba +letter-timeline_minimal 0:a463bced3db2 -0.53 ienjoytranslatingfuzzyworkflowre +letter-timeline_minimal 0:dd474e249222 -0.53 makeadoptioneasierfortherestofth +letter-timeline_minimal 0:a71e98216baa -0.33 iwouldwelcometheopportunitytobri +letter-timeline_minimal 0:80a6a628ad52 -0.33 sincerely, proposal-editorial 0:5205c2a261bc 0.81 northline proposal-editorial 0:da0daaba4b15 0.33 studio proposal-editorial 0:ecd1378bc9dc -0.31 proposal diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv index 879fbd243..386eb1252 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv @@ -24,8 +24,8 @@ cv-sidebar_portrait 1 1 49 49 2.78 3.02 49 cv-slate_orange 1 1 87 86 3.27 3.87 86 cv-teal_pulse 1 1 57 57 0.22 0.64 0 cv-terracotta_rail 1 1 102 102 0.44 0.94 0 -cv-timeline_minimal 2 2 89 86 1.40 1.40 0 -cv-violet_grid 1 2 80 76 5.71 21.81 40 +cv-timeline_minimal 2 2 89 86 0.45 0.50 0 +cv-violet_grid 1 2 80 76 5.21 21.31 39 invoice-classic 1 1 56 56 0.12 0.30 0 invoice-classic_long 2 2 166 166 0.20 0.27 0 invoice-consulting 1 1 92 92 0.67 0.96 3 @@ -55,7 +55,7 @@ letter-monogram_sidebar 1 1 12 12 0.23 0.37 0 letter-nordic_clean 1 1 15 15 0.16 0.38 0 letter-panel 1 1 13 13 0.15 0.21 0 letter-sidebar_portrait 1 1 12 12 0.43 0.51 0 -letter-timeline_minimal 1 1 15 15 1.32 1.52 0 +letter-timeline_minimal 1 1 15 15 0.33 0.53 0 proposal-editorial 2 2 132 129 44.55 45.38 72 proposal-indigo 1 1 72 70 0.26 0.65 0 proposal-modern 1 1 44 44 0.50 0.75 0 diff --git a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java index 34eeeb199..bf9e6f7e5 100644 --- a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java +++ b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java @@ -224,7 +224,8 @@ public final class DocxSemanticBackend implements SemanticBackend { private double hangingOverBy; // How far the last line of the cell written last hangs below everything the cell writes // under it, in points: Word's cell is that much taller than its content on the page (see - // writeInCell). The row it stands in reads it. + // writeInCell). A row reads it for each of its cells (writeRow); a panel or a stack written + // as columns does not, and that much still makes its table taller. private double cellOverhang; /** @@ -10507,13 +10508,23 @@ private void writeSpacer(XWPFDocument document, SpacerNode node) throws Exceptio writeInPlaceOf(document, node); return; } + boolean opensAPage = currentCell == null && overlayDepth == 0 && startsAPageOfItsOwn(node); + if (opensAPage) { + // Text hanging below the block before — a row's last line held to its icon — a pull + // out of it and a card's border below it stay on the page above with it, as at a + // paragraph moved to a new page (holdAParagraphsTopEdgeOnItsPage), whether or not the + // spacer's line then holds any of the gap. + forgetTheHang(); + pullBelow = 0; + borderBelow = 0; + } XWPFParagraph para = newBodyParagraph(document); para.createRun().setText(""); // The spacer is its height and nothing more. An empty paragraph at Word's own line // height is a line of text tall, and it stood on top of that height: between two CV // entries held apart by a 4.5pt spacer, the page shows 16pt and LibreOffice drew 23. holdToHairline(para); - if (currentCell == null && overlayDepth == 0 && startsAPageOfItsOwn(node)) { + if (opensAPage) { holdTheGapAboveInTheLine(para, node); } // The hairline is part of the height, as a separator's is of the gap it stands in: owed diff --git a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxRowOverhangTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxRowOverhangTest.java index b8a92f1c4..57860b59f 100644 --- a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxRowOverhangTest.java +++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxRowOverhangTest.java @@ -101,6 +101,77 @@ void aHangingLineTakesItsReachOutOfItsCellsPaddingFirst() throws Exception { } } + @Test + void aRowsHangStaysOnItsPageWhenTheGapUnderItCarriesASpacerToTheNext() throws Exception { + // What the row's last line hangs past the row is on the page above. + try (XWPFDocument document = aSpacerOpeningTheNextPageUnder(page -> page.addRow(row -> row.name("Block") + .addSection("Left", left -> left.addParagraph(p -> p.textStyle(SMALL).text("Name"))) + .addSection("Right", DocxRowOverhangTest::contact)))) { + XWPFTableCell cell = document.getTables().get(0).getRow(0).getCell(1); + XWPFParagraph iconed = cell.getParagraphs().get(cell.getParagraphs().size() - 1); + assertThat(iconed.getCTP().getPPr().getSpacing().getLineRule()).as("the row's last line hangs") + .isEqualTo(org.openxmlformats.schemas.wordprocessingml.x2006.main.STLineSpacingRule.EXACT); + + assertThat(DocxTwips.of(spacerAbove(after(document)).getLine())).as("the hairline and the whole gap") + .isEqualTo(2 + Math.round(GAP * 20)); + } + } + + @Test + void aPullOutOfTheBlockAboveStaysOnItsPageWhenTheGapCarriesASpacerToTheNext() throws Exception { + // The paragraph above pulls what follows it 4pt up, on its own page. + try (XWPFDocument document = aSpacerOpeningTheNextPageUnder(page -> page.addParagraph(p -> p.name("Block") + .textStyle(SMALL).text("Pulled").margin(new DocumentInsets(0, 0, -4, 0))))) { + assertThat(DocxTwips.of(spacerAbove(after(document)).getLine())).as("the hairline and the whole gap") + .isEqualTo(2 + Math.round(GAP * 20)); + } + } + + /** + * A block ending 3pt above the page's foot, less than the 10pt gap under it, then a 3pt spacer + * and a paragraph: the gap and the spacer open the next page, which the layout is checked to + * do before the export is read. + */ + private static XWPFDocument aSpacerOpeningTheNextPageUnder(Consumer block) + throws Exception { + double extent; + try (DocumentSession session = blockAndSpacer(0, block)) { + com.demcha.compose.document.layout.PlacedNode placed = placed(session, "Block"); + extent = placed.placementHeight() + placed.margin().top() + placed.margin().bottom(); + } + try (DocumentSession session = blockAndSpacer(400 - 2 * 20 - 3 - GAP - extent, block)) { + com.demcha.compose.document.layout.PlacedNode spacer = placed(session, "Gap"); + assertThat(spacer.startPage()).as("the spacer opens the next page").isEqualTo(1); + assertThat(400 - 20 - spacer.placementY() - spacer.placementHeight()) + .as("with the gap above it there").isCloseTo(GAP, org.assertj.core.api.Assertions.within(0.01)); + return new XWPFDocument(new ByteArrayInputStream(session.export(new DocxSemanticBackend()))); + } + } + + private static DocumentSession blockAndSpacer(double filler, Consumer block) { + DocumentSession session = GraphCompose.document().pageSize(400, 400).margin(DocumentInsets.of(20)).create(); + session.pageFlow(page -> { + page.spacing(GAP); + if (filler > 0) { + page.spacer(0, filler); + } + block.accept(page); + page.addSpacer(spacer -> spacer.name("Gap").size(0, 3)) + .addParagraph(p -> p.textStyle(SMALL).text("After")); + }); + return session; + } + + private static com.demcha.compose.document.layout.PlacedNode placed(DocumentSession session, String name) { + return session.layoutGraph().nodes().stream() + .filter(node -> name.equals(node.semanticName())).findFirst().orElseThrow(); + } + + private static CTSpacing spacerAbove(XWPFParagraph paragraph) { + java.util.List paragraphs = paragraph.getDocument().getParagraphs(); + return paragraphs.get(paragraphs.indexOf(paragraph) - 1).getCTP().getPPr().getSpacing(); + } + /** A contact stack whose last line holds an icon lowered as TimelineMinimal's are. */ private static void contact(SectionBuilder section) { section.spacing(3);