Conclusio/matlab-epsclean

example where epsclean leaves a few white lines

morattico opened this issue ยท 14 comments

This project is a great service to the community. Thanks! I am attaching an example where epsclean removes most, but not all of the artifacts. The original file is fig1.eps and the cleaned version is fig1c.eps (attached inside a zip-file). I have also attached the pdfs (created by Preview on a mac). I tried the four different combinations of true/false for the optional epsclean arguments and they all showed the same behavior. Thanks for your help. -Matt

fig1.pdf
fig1c.pdf
fig1.zip

Thank you for bringing this to my attention!

First of all, I can reproduce the behavior. I looked into it and isolated one of the white lines. The definition in the PostScript (eps) file on its own is perfectly reasonable. So for example in the file there are two polygons next to each other, having the same filling and sharing some vertices.

This brings me to the actual problem. The problem seems to be not really the eps file or Matlab for that matter, but the anti-aliasing behavior of some viewers. E.g. in your case Preview, in my case Foxit. In Illustrator it is fine for example. I looked around and it seems to be a known problem for vector graphics:
http://www.idlcoyote.com/ps_tips/psstripes.html
https://www.pmel.noaa.gov/maillists/tmap/ferret_users/fu_2009/msg00613.html

The way I could solve this is by "merging" adjacent filled polygons by finding the hull of the polygons' vertices. I will look into this, but that might be too much work. So I don't promise anything.

Will comment again when I know more...

Update: I introduced the possibility to merge polygons of the same type together.

Your problem should be fixed by calling:
epsclean('input.eps','output.eps','combineAreas',true);

In case you have problems with the Z-order, use:
epsclean('input.eps','output.eps','combineAreas',true,'groupSoft',true);

Be aware that using the parameter 'combineAreas' is quite time consuming. The fixing/conversion process takes about 10 times longer..

I am using Preview on Mac too. I tried using the 'combineAreas' option on fig1.eps posted by morattico, and that seems to fix all issues with that file. However if I try the following:
z = peaks; contourf(z); print(gcf,'-depsc','-painters','out.eps'); epsclean('out.eps','combineAreas',true);

Then I get a new set of artifacts: out

Any ideas on this one? Seems to almost work.
Fantastic project by the way, thanks for your support!

Thank you for your tests.

It seems to be viewer application specific (some anti-aliasing behavior). The artifacts you see are exactly the lines I introduced to split up filled areas containing other filled areas. With the viewers I used so far I did not see these lines.

One solution to this specific problem would be to check whether or not areas are completely inside other areas (+ check if they are in front) and then draw them back-to-front with each area completely filled.

But I cannot imagine that this works with all use cases and it is not trivial to implement, so this will not be fixed from my side. I will still reopen this issue in case someone else finds a solution.

Yeh, if I convert to PDF then view with Acrobat Reader, then I do not see the artifacts (seems to be specific to Preview for me).

But as another observation (if anyone else out there is battling with this): the cleaning appears to work fine if I plot on an axesm. e.g.:
z = peaks;
axesm('mercator')
contourfm(1:49,1:49,z)
print(gcf,'-depsc','-painters','out.eps');
epsclean('out.eps');

This presents no issues for me with Preview. Note that the axesm function is part of the Mapping Toolbox.

Thanks again for the time you've spent on this @Conclusio. What you have provided so far is fantastic, and greatly appreciated!

I just wanted to share an update, since all of my issues have been resolved with the fix on this thread: #11

Referring to my post from 14 Sep 2017 above, those white lines persisted.
But now I can this procedure to obtain a clean image:
z = peaks;
contourf(z);
print(gcf,'-depsc','-painters','out.eps');
epsclean('out.eps');

Next, in the "cleaned" eps file (open with a text editor), change line
/f/fill ld
to
/f{GS 0.01 LW S GR fill}bd
and move it below the /LW... line

This works on everything I've tested. Viewing in Preview on Mac, and I'm running Matlab 2017a.

Thank you for the feedback.
I added this procedure by adding "/f{GS ..." to the epsclean script.

It can be activated by using: epsclean(... , 'closeGaps',true);
This will draw additional lines with a default width of 0.01.

As this is considered a workaround I will NOT CLOSE this issue. Maybe someone comes up with a better way of fixing this.

Thanks very much! However, it does not appear to be working for me.. It does not make the change to the eps file if I call epsclean(... , 'closeGaps',true);

Taking a quick look in to the code, it appears closeGaps is set to false by default, but I can't see any code that changes it to the varargin value..

It's being assigned in line 140. The test cases for the parameter are also successful...

Please check again that you're looking at the correct file. And if so, maybe you could provide your original eps file?

But it's not being assigned in the block line 109-122. Therefore it always takes the default value. Sorry if I'm missing something really obvious here..

However, if I change that default assignment to 'true' at line 103, then it works fine. But having said that, it does not move the "/f{GS.." line to below "/LW/setlinewidth ld", it simply replaces the "/f/fill ld" line. Does not seem to matter, but was that the intended result?

My original eps file is created with:
z = peaks;
contourf(z);
print(gcf,'-depsc','-painters','out.eps');

Still cannot reproduce your problem.
I cloned the repository completely fresh and restarted Matlab.

Created this Matlab script right next to "epsclean.m":
cleantestGaps.txt
(rename it to cleantestGaps.m)

Ran the script and it's working just fine.... - Please check and report.

Regarding your questions:
I'm using Matlab's inputParser. The lines 109-122 are legacy code (to support the old parameters to epsclean without breaking the API).

"/LW/setlinewidth ld" - is stating that the command "setlinewidth" shall be named "LW". It is basically an alternative definition. I don't need to move below this line, because I am not using the statement "LW" anymore, but using "setlinewidth". It's fine, it works either way. I just don't need to implement that much to move below a line...

Oh I see, I have been using the call: epsclean('out.eps','closeGaps',true); rather than say epsclean('out.eps','out_clean.eps','closeGaps',true); (the latter works fine).

But why does say epsclean('out.eps','combineAreas',true); work fine, but not epsclean('out.eps','closeGaps',true);?

Now I see too :)
I'm sorry. I didn't take enough care when introducing the new parameters...

Thanks a lot for finding this bug!
I did submit a fix

Confirming that it now behaves as expected.

Huge thankyou to you too! The number of hours (rather, weeks) that I've spent on work-arounds for this Matlab issue... I now have a clean and easy-to-implement solution. It is really hugely appreciated!