1   package nl.dedicon.pipeline.braille.calabash.impl;
2   
3   import com.xmlcalabash.core.XProcException;
4   import com.xmlcalabash.core.XProcRuntime;
5   import com.xmlcalabash.core.XProcStep;
6   import com.xmlcalabash.library.DefaultStep;
7   import com.xmlcalabash.runtime.XAtomicStep;
8   import java.io.File;
9   import java.io.IOException;
10  import java.net.URI;
11  import java.net.URISyntaxException;
12  import java.nio.file.Files;
13  import java.nio.file.Path;
14  import java.util.Arrays;
15  import net.sf.saxon.s9api.QName;
16  import net.sf.saxon.s9api.SaxonApiException;
17  import org.apache.commons.lang3.StringUtils;
18  import org.daisy.common.xproc.calabash.XProcStepProvider;
19  import org.osgi.service.component.annotations.Component;
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  
23  /**
24   * XProc step for replacing double form feeds in BRL files
25   * 
26   * @author Paul Rambags
27   */
28  public class BrlReplaceDoubleFormFeedsStep extends DefaultStep {
29  
30      private static final Logger logger = LoggerFactory.getLogger(BrlReplaceDoubleFormFeedsStep.class);
31  
32      private static final QName _brl_uri = new QName("brl-uri");
33  
34      private BrlReplaceDoubleFormFeedsStep(XProcRuntime runtime, XAtomicStep step) {
35          super(runtime, step);
36      }
37  
38      @Override
39      public void reset() {
40      }
41  
42      @Override
43      public void run() throws SaxonApiException {
44          super.run();
45  
46          try {
47              String brlUri = getOption(_brl_uri, "");
48              if (StringUtils.isNotBlank(brlUri)) {
49                  
50                  try {
51  
52                      Path path = new File(new URI(brlUri)).toPath();
53                      // The path refers to a CP850 encoded file
54                      // A sequence of FF (LF)* FF is replaced by FF
55                      byte[] inBytes = Files.readAllBytes(path);
56                      byte[] outBytes = new byte[inBytes.length];
57                      
58                      int inIndex = 0;
59                      int outIndex = 0;
60                      while (inIndex < inBytes.length) {
61                          int doubleFormFeedLength = findDoubleFormFeed(inBytes, inIndex);
62                          if (doubleFormFeedLength > 0) {
63                              outBytes[outIndex] = 0xC; // form feed
64                              inIndex += doubleFormFeedLength;
65                              outIndex ++;
66                          } else {
67                              outBytes[outIndex] = inBytes[inIndex];
68                              inIndex ++;
69                              outIndex ++;
70                          }
71                      }
72                      
73                      Files.write(path, Arrays.copyOfRange(outBytes, 0, outIndex));
74  
75                  } catch (URISyntaxException|IOException e) {
76                      logger.error(String.format("Failed to replace double form feeds in file %s due to %s", brlUri, e.getMessage()));
77                      throw e;
78                  }
79              }
80          } catch (Exception e) {
81              logger.error("dedicon:brl-replace-double-form-feeds failed", e);
82              throw new XProcException(step.getNode(), e);
83          }
84      }
85  
86      // finds a C(DA)*C sequence and returns it's length
87      private int findDoubleFormFeed(byte[] bytes, int index) {
88          int length = 0;
89          if (index + length < bytes.length && bytes[index + length] == 0xC) {
90              length ++;
91              while (index + length < bytes.length + 1 && bytes[index + length] == 0xD && bytes[index + length + 1] == 0xA) {
92                  length += 2;
93              }
94              if (index + length < bytes.length && bytes[index + length] == 0xC) {
95                  length ++;
96                  return length;
97              }
98          }
99          return -1;
100     }
101     
102     @Component(
103             name = "dedicon:brl-replace-double-form-feeds",
104             service = {XProcStepProvider.class},
105             property = {"type:String={http://www.dedicon.nl}brl-replace-double-form-feeds"}
106     )
107     public static class Provider implements XProcStepProvider {
108 
109         @Override
110         public XProcStep newStep(XProcRuntime runtime, XAtomicStep step) {
111             return new BrlReplaceDoubleFormFeedsStep(runtime, step);
112         }
113     }
114 }