├── .mvn ├── jvm.config ├── maven.config ├── wrapper │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── extensions.xml └── settings.xml ├── .github ├── FUNDING.yml └── workflows │ ├── sonatype.yaml │ ├── ci.yaml │ ├── codeql.yaml │ └── coveralls.yaml ├── src ├── test │ ├── resources │ │ ├── html │ │ │ ├── testSimpleDoctypeResult.html │ │ │ ├── testEnabled.html │ │ │ ├── testEnabledResult.html │ │ │ ├── testPreserveLineBreaksResult.html │ │ │ ├── testRemoveStyleAttributesResult.html │ │ │ ├── testRemoveFormAttributesResult.html │ │ │ ├── testSurroundingSpacesResult.html │ │ │ ├── testSurroundingSpaces.html │ │ │ ├── testRemoveInputAttributesResult.html │ │ │ ├── testRemoveMultiSpaces.html │ │ │ ├── testRemoveMultiSpacesResult.html │ │ │ ├── testRemoveScriptAttributesResult.html │ │ │ ├── testRemoveFormAttributes.html │ │ │ ├── testCompressCssResult.html │ │ │ ├── testRemoveInputAttributes.html │ │ │ ├── testRemoveJavaScriptProtocolResult.html │ │ │ ├── testPreservePatternsResult.html │ │ │ ├── testRemoveStyleAttributes.html │ │ │ ├── testSimpleDoctype.html │ │ │ ├── testRemoveJavaScriptProtocol.html │ │ │ ├── testSimpleBooleanAttributesResult.html │ │ │ ├── testPreserveLineBreaks.html │ │ │ ├── testRemoveSpacesInsideTagsResult.html │ │ │ ├── testSimpleBooleanAttributes.html │ │ │ ├── testCompressCss.html │ │ │ ├── testRemoveQuotesResult.html │ │ │ ├── testRemoveSpacesInsideTags.html │ │ │ ├── testPreservePatterns.html │ │ │ ├── testRemoveIntertagSpacesResult.html │ │ │ ├── testRemoveLinkAttributesResult.html │ │ │ ├── testRemoveQuotes.html │ │ │ ├── testRemoveLinkAttributes.html │ │ │ ├── testRemoveIntertagSpaces.html │ │ │ ├── testRemoveScriptAttributes.html │ │ │ ├── testRemoveHttpProtocolResult.html │ │ │ ├── testRemoveHttpsProtocolResult.html │ │ │ ├── testRemoveHttpProtocol.html │ │ │ ├── testRemoveHttpsProtocol.html │ │ │ ├── testCompressJavaScriptYuiResult.html │ │ │ ├── testRemoveCommentsResult.html │ │ │ ├── testCompressJavaScriptClosureResult.html │ │ │ ├── testCompressResult.html │ │ │ ├── testCompressJavaScript.html │ │ │ ├── testRemoveComments.html │ │ │ └── testCompress.html │ │ └── xml │ │ │ ├── testEnabled.xml │ │ │ ├── testEnabledResult.xml │ │ │ ├── testRemoveCommentsResult.xml │ │ │ ├── testRemoveComments.xml │ │ │ ├── testCompressResult.xml │ │ │ ├── testRemoveIntertagSpacesResult.xml │ │ │ ├── testCompress.xml │ │ │ └── testRemoveIntertagSpaces.xml │ └── java │ │ └── com │ │ └── googlecode │ │ └── htmlcompressor │ │ └── compressor │ │ └── XmlCompressorTest.java ├── site │ ├── resources │ │ └── images │ │ │ ├── hazendaz.png │ │ │ └── hazendaz-banner.jpg │ └── site.xml └── main │ ├── java │ ├── jargs │ │ └── gnu │ │ │ └── package-info.java │ └── com │ │ └── googlecode │ │ └── htmlcompressor │ │ ├── package-info.java │ │ ├── taglib │ │ ├── package-info.java │ │ ├── CssCompressorTag.java │ │ ├── XmlCompressorTag.java │ │ └── JavaScriptCompressorTag.java │ │ ├── analyzer │ │ ├── package-info.java │ │ └── HtmlAnalyzer.java │ │ ├── velocity │ │ ├── package-info.java │ │ ├── XmlCompressorDirective.java │ │ ├── CssCompressorDirective.java │ │ ├── JavaScriptCompressorDirective.java │ │ └── HtmlCompressorDirective.java │ │ └── compressor │ │ ├── package-info.java │ │ ├── Compressor.java │ │ ├── YuiCssCompressor.java │ │ ├── HtmlCompressorStatistics.java │ │ ├── HtmlMetrics.java │ │ ├── XmlCompressor.java │ │ ├── YuiJavaScriptCompressor.java │ │ └── ClosureJavaScriptCompressor.java │ ├── assembly │ └── distributive.xml │ └── resources │ └── META-INF │ └── htmlcompressor.tld ├── .gitignore ├── renovate.json ├── .gitattributes ├── LICENSE_HEADER ├── TODO.md ├── format.xml ├── README.md ├── mvnw.cmd ├── pom.xml ├── LICENSE ├── CHANGELOG.md └── mvnw /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [hazendaz] 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testSimpleDoctypeResult.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/xml/testEnabled.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testEnabled.html: -------------------------------------------------------------------------------- 1 | html html 2 | -------------------------------------------------------------------------------- /src/test/resources/xml/testEnabledResult.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testEnabledResult.html: -------------------------------------------------------------------------------- 1 | html html 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testPreserveLineBreaksResult.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveStyleAttributesResult.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveFormAttributesResult.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /src/test/resources/html/testSurroundingSpacesResult.html: -------------------------------------------------------------------------------- 1 | aaa

bbbb
ccccccc

ddddd eeeeeeee 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testSurroundingSpaces.html: -------------------------------------------------------------------------------- 1 | aaa

bbbb
ccccccc

ddddd eeeeeeee 2 | -------------------------------------------------------------------------------- /src/test/resources/xml/testRemoveCommentsResult.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | ]]> 5 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveInputAttributesResult.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/site/resources/images/hazendaz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hazendaz/htmlcompressor/HEAD/src/site/resources/images/hazendaz.png -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveMultiSpaces.html: -------------------------------------------------------------------------------- 1 |
           
2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveMultiSpacesResult.html: -------------------------------------------------------------------------------- 1 |
           
2 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Daether.checksums.algorithms=SHA-512,SHA-256,SHA-1,MD5 2 | -Daether.connector.smartChecksums=false 3 | --no-transfer-progress 4 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveScriptAttributesResult.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/site/resources/images/hazendaz-banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hazendaz/htmlcompressor/HEAD/src/site/resources/images/hazendaz-banner.jpg -------------------------------------------------------------------------------- /src/test/resources/xml/testRemoveComments.xml: -------------------------------------------------------------------------------- 1 |
2 | 4 | ]]> 5 | 6 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveFormAttributes.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testCompressCssResult.html: -------------------------------------------------------------------------------- 1 |
2 | 	
6 | 
7 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveInputAttributes.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveJavaScriptProtocolResult.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.classpath 3 | /.project 4 | /target 5 | .mvn/wrapper/maven-wrapper.jar 6 | pom.xml.releaseBackup 7 | release.properties 8 | .factorypath 9 | -------------------------------------------------------------------------------- /src/test/resources/html/testPreservePatternsResult.html: -------------------------------------------------------------------------------- 1 | ?><% %> 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveStyleAttributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/test/resources/html/testSimpleDoctype.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveJavaScriptProtocol.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testSimpleBooleanAttributesResult.html: -------------------------------------------------------------------------------- 1 | checked="checked" 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testPreserveLineBreaks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveSpacesInsideTagsResult.html: -------------------------------------------------------------------------------- 1 | qwe = eee
2 | -------------------------------------------------------------------------------- /src/test/resources/html/testSimpleBooleanAttributes.html: -------------------------------------------------------------------------------- 1 | checked="checked" 2 | -------------------------------------------------------------------------------- /src/test/resources/xml/testCompressResult.xml: -------------------------------------------------------------------------------- 1 |
2 | 4 | ]]> 5 | aaa bbbb 6 | -------------------------------------------------------------------------------- /src/test/resources/html/testCompressCss.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
 7 | 	
11 | 
12 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveQuotesResult.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveSpacesInsideTags.html: -------------------------------------------------------------------------------- 1 | qwe = eee
2 | -------------------------------------------------------------------------------- /src/test/resources/html/testPreservePatterns.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | ?> 4 | <% %> 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveIntertagSpacesResult.html: -------------------------------------------------------------------------------- 1 |
           
2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveLinkAttributesResult.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/xml/testRemoveIntertagSpacesResult.xml: -------------------------------------------------------------------------------- 1 |
2 | 4 | ]]> 5 | aaa bbbb 6 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveQuotes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/resources/xml/testCompress.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | ]]> 7 | 8 | 9 | 10 | 11 | aaa bbbb 12 | -------------------------------------------------------------------------------- /src/test/resources/xml/testRemoveIntertagSpaces.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | ]]> 7 | 8 | 9 | 10 | 11 | aaa bbbb 12 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended", 5 | "helpers:pinGitHubActionDigests" 6 | ], 7 | "baseBranchPatterns": [ 8 | "master", 9 | "javax" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveLinkAttributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveIntertagSpaces.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
           
4 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveScriptAttributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=source 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar 4 | wrapperVersion=3.3.4 5 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveHttpProtocolResult.html: -------------------------------------------------------------------------------- 1 |
http://leave 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveHttpsProtocolResult.html: -------------------------------------------------------------------------------- 1 | http://leave 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveHttpProtocol.html: -------------------------------------------------------------------------------- 1 | http://leave 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveHttpsProtocol.html: -------------------------------------------------------------------------------- 1 | http://leave 2 | -------------------------------------------------------------------------------- /src/test/resources/html/testCompressJavaScriptYuiResult.html: -------------------------------------------------------------------------------- 1 |
2 | 	
7 | 
8 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveCommentsResult.html: -------------------------------------------------------------------------------- 1 |
 aaa  bbb 
5 | -------------------------------------------------------------------------------- /src/test/resources/html/testCompressJavaScriptClosureResult.html: -------------------------------------------------------------------------------- 1 |
2 | 	
7 | 
8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /src/test/resources/html/testCompressResult.html: -------------------------------------------------------------------------------- 1 |
           
               
2 | -------------------------------------------------------------------------------- /src/test/resources/html/testCompressJavaScript.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 |
16 | 	
21 | 
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/test/resources/html/testRemoveComments.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
 aaa  bbb 
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/test/resources/html/testCompress.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
           
4 | 5 | 6 |
               
7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE_HEADER: -------------------------------------------------------------------------------- 1 | Copyright ${license.git.copyrightYears} the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | https://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Ideas for improvements that require brainstorming: 2 | 3 | - Get rid of recursion in conditional comments 4 | - Better choice of output stream (System.out vs System.error) for command line compressor to separate compression results from error reports and help screens 5 | - Better exception handling in java api. Should compression throw any exceptions or catch them? 6 | - Look into error logging (java.util.logging, log4j, slf4j bridge?) 7 | - Look into possibility of creating complex js/css/html compression solution that produces googleplus-like compression results [shrinking css classes](https://github.com/hazendaz/htmlcompressor/issues/46) 8 | 9 | If you have any suggestions about features listed (or not listed) above please create an [Issue report](https://github.com/hazendaz/htmlcompressor/issues). 10 | -------------------------------------------------------------------------------- /src/main/java/jargs/gnu/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Jargs GNU package. 18 | */ 19 | package jargs.gnu; 20 | -------------------------------------------------------------------------------- /format.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * HtmlCompressor Package. 18 | */ 19 | package com.googlecode.htmlcompressor; 20 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/taglib/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Taglib Package. 18 | */ 19 | package com.googlecode.htmlcompressor.taglib; 20 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/analyzer/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Analyzer Package. 18 | */ 19 | package com.googlecode.htmlcompressor.analyzer; 20 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/velocity/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Velocity Package. 18 | */ 19 | package com.googlecode.htmlcompressor.velocity; 20 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/compressor/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Compressor Package. 18 | */ 19 | package com.googlecode.htmlcompressor.compressor; 20 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | fr.jcgay.maven 22 | maven-profiler 23 | 3.3 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/sonatype.yaml: -------------------------------------------------------------------------------- 1 | name: Sonatype 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: read-all 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | build: 16 | if: github.repository_owner == 'hazendaz' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 30 19 | steps: 20 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 21 | - name: Setup Java 22 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5 23 | with: 24 | cache: maven 25 | distribution: temurin 26 | java-version: 25 27 | - name: Deploy to Sonatype 28 | run: ./mvnw deploy --batch-mode --no-transfer-progress --settings ./.mvn/settings.xml --show-version -Dlicense.skip=true -DskipTests 29 | env: 30 | CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} 31 | CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} 32 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [workflow_dispatch, push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | test: 13 | runs-on: ${{ matrix.os }} 14 | timeout-minutes: 30 15 | strategy: 16 | matrix: 17 | cache: [maven] 18 | distribution: [temurin] 19 | java: [21, 25, 26-ea] 20 | os: [macos-latest, ubuntu-latest, windows-latest] 21 | fail-fast: false 22 | max-parallel: 6 23 | name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} 24 | 25 | steps: 26 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 27 | - name: Setup Java ${{ matrix.java }} ${{ matrix.distribution }} 28 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5 29 | with: 30 | cache: ${{ matrix.cache }} 31 | distribution: ${{ matrix.distribution }} 32 | java-version: ${{ matrix.java }} 33 | - name: Test with Maven 34 | run: ./mvnw test --batch-mode --no-transfer-progress --show-version -D"license.skip=true" 35 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/compressor/Compressor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.compressor; 17 | 18 | /** 19 | * Interface describing compressor classes. 20 | */ 21 | public interface Compressor { 22 | /** 23 | * The main method that compresses the given source and returns a compressed result. 24 | * 25 | * @param source 26 | * The source to compress. 27 | * 28 | * @return Compressed result. 29 | */ 30 | public abstract String compress(String source); 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yaml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | schedule: 9 | - cron: '43 10 * * 2' 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze 18 | runs-on: 'ubuntu-latest' 19 | timeout-minutes: 30 20 | permissions: 21 | actions: read 22 | contents: read 23 | security-events: write 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 28 | 29 | - name: Setup Java 30 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5 31 | with: 32 | cache: maven 33 | distribution: 'temurin' 34 | java-version: 25 35 | 36 | - name: Initialize CodeQL 37 | uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4 38 | with: 39 | queries: +security-and-quality 40 | 41 | - name: Autobuild 42 | uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4 43 | 44 | - name: Perform CodeQL Analysis 45 | uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4 46 | -------------------------------------------------------------------------------- /.github/workflows/coveralls.yaml: -------------------------------------------------------------------------------- 1 | name: Coveralls 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | coveralls: 13 | if: github.repository_owner == 'hazendaz' 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 30 16 | steps: 17 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 18 | - name: Setup Java 19 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5 20 | with: 21 | cache: maven 22 | distribution: temurin 23 | java-version: 25 24 | - name: Run the build 25 | run: ./mvnw test --batch-mode --no-transfer-progress --quiet --show-version -Dlicense.skip=true 26 | - name: Report Coverage to Coveralls for Pull Requests 27 | if: github.event_name == 'pull_request' 28 | run: ./mvnw generate-sources jacoco:report coveralls:report --batch-mode --no-transfer-progress -DpullRequest=${{ env.PR_NUMBER }} -DrepoToken=${{ env.GITHUB_TOKEN }} -DserviceName=github 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | PR_NUMBER: ${{ github.event.number }} 32 | - name: Report Coverage to Coveralls for General Push 33 | if: github.event_name == 'push' 34 | run: ./mvnw generate-sources jacoco:report coveralls:report --batch-mode --no-transfer-progress -DrepoToken=${{ env.GITHUB_TOKEN }} -DserviceName=github 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.skins 29 | maven-fluido-skin 30 | 2.1.0 31 | 32 | 33 | 34 | true 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /.mvn/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 21 | 22 | 23 | 24 | 25 | central 26 | ${env.CI_DEPLOY_USERNAME} 27 | ${env.CI_DEPLOY_PASSWORD} 28 | 29 | 30 | 31 | 32 | gh-pages-scm 33 | 34 | branch 35 | gh-pages 36 | 37 | 38 | 39 | 40 | 41 | github 42 | ${env.GITHUB_TOKEN} 43 | 44 | 45 | 46 | 47 | nvd 48 | ${env.NVD_API_KEY} 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/assembly/distributive.xml: -------------------------------------------------------------------------------- 1 | 18 | 20 | 21 | distribution 22 | 23 | 24 | zip 25 | 26 | 27 | 28 | 29 | false 30 | lib 31 | false 32 | false 33 | 34 | *:jar 35 | 36 | 37 | 38 | 39 | 40 | 41 | ${project.basedir}/src 42 | 43 | 44 | ${project.basedir} 45 | 46 | pom.xml 47 | *.txt 48 | 49 | 50 | README.md 51 | 52 | 53 | 54 | ${project.build.directory} 55 | bin 56 | 57 | *.jar 58 | 59 | 60 | 61 | ${project.build.directory}/apidocs 62 | doc 63 | 64 | 65 | 66 | 67 | README.md 68 | true 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HtmlCompressor # 2 | 3 | [![Java CI](https://github.com/hazendaz/htmlcompressor/workflows/Java%20CI/badge.svg)](https://github.com/hazendaz/htmlcompressor/actions?query=workflow%3A%22Java+CI%22) 4 | [![Coverage Status](https://coveralls.io/repos/github/hazendaz/htmlcompressor/badge.svg?branch=master)](https://coveralls.io/github/hazendaz/htmlcompressor?branch=master) 5 | [![Maven central](https://maven-badges.herokuapp.com/maven-central/com.github.hazendaz/htmlcompressor/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.hazendaz/htmlcompressor) 6 | [![Apache 2](http://img.shields.io/badge/license-Apache%202-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) 7 | 8 | ![hazendaz](src/site/resources/images/hazendaz-banner.jpg) 9 | 10 | See site page [here](https://hazendaz.github.io/htmlcompressor/) 11 | 12 | Small, fast and very easy to use Java library that minifies given HTML or XML source by removing extra whitespaces, comments and other unneeded characters without breaking the content structure. As a result pages become smaller in size and load faster. A command-line version of the compressor is also available. 13 | 14 | ## PACKAGE CONTENT ## 15 | - ```/bin``` contains main ${project.artifactId}-${project.version}.jar binary, as well as several extra jars (could be useful for IDE integration) 16 | - ```/doc``` javadocs 17 | - ```/src``` sources 18 | - ```/lib``` dependencies (for using with a command line compressor or non-Maven projects) 19 | - ```pom.xml``` Maven POM file 20 | 21 | ## USAGE ## 22 | - For java projects add ${project.artifactId}-${project.version}.jar library to your project's classpath 23 | - For a command line usage run: ```java -jar ${project.artifactId}-${project.version}.jar -h``` to get a brief description of available parameters. 24 | 25 | Please refer to http://code.google.com/p/htmlcompressor/ for the detailed documentation. 26 | 27 | ## PROJECT BUILD ## 28 | - Install JDK 11+ (https://www.oracle.com/java/technologies/downloads/) 29 | - Install Maven 3.9.1+ (http://maven.apache.org/download.html) 30 | - Run build.bat or build.sh 31 | - Compiled binaries will be placed in /target subdirectory 32 | 33 | ## CHANGELOG ## 34 | - Changelog for 1.5.3 and before found [here](CHANGELOG.md) 35 | 36 | ## Looking to help ## 37 | - A lot of docuemntation is still at google code, a good getting started pull request would be to work on translate that data over. The original export only exported the issues and source code. Thus far the TODO wiki is 100% copied over, The change log is copied over too but missing links. Need all documentation copied over. 38 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/compressor/YuiCssCompressor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.compressor; 17 | 18 | import com.yahoo.platform.yui.compressor.CssCompressor; 19 | 20 | import java.io.IOException; 21 | import java.io.StringReader; 22 | import java.io.StringWriter; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | /** 28 | * Basic CSS compressor implementation using Yahoo YUI 29 | * Compressor that could be used by {@link HtmlCompressor} for inline CSS compression. 30 | * 31 | * @see HtmlCompressor#setCssCompressor(Compressor) 32 | * @see Yahoo YUI Compressor 33 | */ 34 | public class YuiCssCompressor implements Compressor { 35 | 36 | /** The Constant Logger. */ 37 | private static final Logger logger = LoggerFactory.getLogger(YuiCssCompressor.class); 38 | 39 | /** The line break. */ 40 | private int lineBreak = -1; 41 | 42 | @Override 43 | public String compress(String source) { 44 | StringWriter result = new StringWriter(); 45 | 46 | try { 47 | CssCompressor compressor = new CssCompressor(new StringReader(source)); 48 | compressor.compress(result, lineBreak); 49 | } catch (IOException e) { 50 | result.write(source); 51 | logger.error("", e); 52 | } 53 | 54 | return result.toString(); 55 | } 56 | 57 | /** 58 | * Returns number of symbols per line Yahoo YUI Compressor will use during CSS compression. This corresponds to 59 | * --line-break command line option. 60 | * 61 | * @return line-break parameter value used for CSS compression. 62 | * 63 | * @see Yahoo YUI Compressor 64 | */ 65 | public int getLineBreak() { 66 | return lineBreak; 67 | } 68 | 69 | /** 70 | * Tells Yahoo YUI Compressor to break lines after the specified number of symbols during CSS compression. This 71 | * corresponds to --line-break command line option. This option has effect only if CSS compression is 72 | * enabled. Default is -1 to disable line breaks. 73 | * 74 | * @param lineBreak 75 | * set number of symbols per line 76 | * 77 | * @see Yahoo YUI Compressor 78 | */ 79 | public void setLineBreak(int lineBreak) { 80 | this.lineBreak = lineBreak; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/velocity/XmlCompressorDirective.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.velocity; 17 | 18 | import com.googlecode.htmlcompressor.compressor.XmlCompressor; 19 | 20 | import java.io.IOException; 21 | import java.io.StringWriter; 22 | import java.io.Writer; 23 | 24 | import org.apache.velocity.context.InternalContextAdapter; 25 | import org.apache.velocity.exception.MethodInvocationException; 26 | import org.apache.velocity.exception.TemplateInitException; 27 | import org.apache.velocity.runtime.RuntimeServices; 28 | import org.apache.velocity.runtime.directive.Directive; 29 | import org.apache.velocity.runtime.parser.node.Node; 30 | 31 | /** 32 | * Velocity directive that compresses an XML content within #compressXml ... #end block. Compression parameters are set 33 | * by default. 34 | * 35 | * @see XmlCompressor 36 | */ 37 | public class XmlCompressorDirective extends Directive { 38 | 39 | /** The Constant xmlCompressor. */ 40 | private static final XmlCompressor xmlCompressor = new XmlCompressor(); 41 | 42 | @Override 43 | public String getName() { 44 | return "compressXml"; 45 | } 46 | 47 | @Override 48 | public int getType() { 49 | return BLOCK; 50 | } 51 | 52 | @Override 53 | public void init(RuntimeServices rs, InternalContextAdapter context, Node node) throws TemplateInitException { 54 | super.init(rs, context, node); 55 | log = rs.getLog(); 56 | 57 | // set compressor properties 58 | xmlCompressor.setEnabled(rs.getBoolean("userdirective.compressXml.enabled", true)); 59 | xmlCompressor.setRemoveComments(rs.getBoolean("userdirective.compressXml.removeComments", true)); 60 | xmlCompressor.setRemoveIntertagSpaces(rs.getBoolean("userdirective.compressXml.removeIntertagSpaces", true)); 61 | } 62 | 63 | @Override 64 | public boolean render(InternalContextAdapter context, Writer writer, Node node) 65 | throws IOException, MethodInvocationException { 66 | 67 | // render content 68 | StringWriter content = new StringWriter(); 69 | node.jjtGetChild(0).render(context, content); 70 | 71 | // compress 72 | try { 73 | writer.write(xmlCompressor.compress(content.toString())); 74 | } catch (Exception e) { 75 | writer.write(content.toString()); 76 | String msg = "Failed to compress content: " + content.toString(); 77 | log.error(msg, e); 78 | throw new RuntimeException(msg, e); 79 | 80 | } 81 | return true; 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/taglib/CssCompressorTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.taglib; 17 | 18 | import com.googlecode.htmlcompressor.compressor.HtmlCompressor; 19 | import com.googlecode.htmlcompressor.compressor.YuiCssCompressor; 20 | 21 | import jakarta.servlet.jsp.JspException; 22 | import jakarta.servlet.jsp.tagext.BodyContent; 23 | import jakarta.servlet.jsp.tagext.BodyTagSupport; 24 | 25 | import java.io.IOException; 26 | 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | /** 31 | * JSP tag that compresses an CSS content within <compress:css> using 32 | * Yahoo YUI Compressor. All CSS-related properties from 33 | * {@link HtmlCompressor} are supported. 34 | * 35 | * @see HtmlCompressor 36 | * @see Yahoo YUI Compressor 37 | */ 38 | @SuppressWarnings("serial") 39 | public class CssCompressorTag extends BodyTagSupport { 40 | 41 | /** The Constant logger. */ 42 | private static final Logger logger = LoggerFactory.getLogger(CssCompressorTag.class); 43 | 44 | /** The enabled. */ 45 | private boolean enabled = true; 46 | 47 | // YUICompressor settings 48 | 49 | /** The yui css line break. */ 50 | private int yuiCssLineBreak = -1; 51 | 52 | @Override 53 | public int doEndTag() throws JspException { 54 | 55 | BodyContent bodyContent = getBodyContent(); 56 | String content = bodyContent.getString(); 57 | 58 | try { 59 | if (enabled) { 60 | // call YUICompressor 61 | YuiCssCompressor compressor = new YuiCssCompressor(); 62 | compressor.setLineBreak(yuiCssLineBreak); 63 | String result = compressor.compress(content); 64 | 65 | bodyContent.clear(); 66 | bodyContent.append(result); 67 | bodyContent.writeOut(pageContext.getOut()); 68 | } else { 69 | bodyContent.clear(); 70 | bodyContent.append(content); 71 | bodyContent.writeOut(pageContext.getOut()); 72 | } 73 | } catch (IOException e) { 74 | logger.error("", e); 75 | } 76 | 77 | return super.doEndTag(); 78 | } 79 | 80 | /** 81 | * Sets the yui css line break. 82 | * 83 | * @param yuiCssLineBreak 84 | * the new yui css line break 85 | * 86 | * @see HtmlCompressor#setYuiCssLineBreak(int) 87 | */ 88 | public void setYuiCssLineBreak(int yuiCssLineBreak) { 89 | this.yuiCssLineBreak = yuiCssLineBreak; 90 | } 91 | 92 | /** 93 | * Sets the enabled. 94 | * 95 | * @param enabled 96 | * the new enabled 97 | * 98 | * @see HtmlCompressor#setEnabled(boolean) 99 | */ 100 | public void setEnabled(boolean enabled) { 101 | this.enabled = enabled; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/taglib/XmlCompressorTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.taglib; 17 | 18 | import com.googlecode.htmlcompressor.compressor.XmlCompressor; 19 | 20 | import jakarta.servlet.jsp.JspException; 21 | import jakarta.servlet.jsp.tagext.BodyContent; 22 | import jakarta.servlet.jsp.tagext.BodyTagSupport; 23 | 24 | import java.io.IOException; 25 | 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | /** 30 | * JSP tag that compresses an XML content within <compress:xml>. Compression parameters are set by default. 31 | * 32 | * @see XmlCompressor 33 | */ 34 | public class XmlCompressorTag extends BodyTagSupport { 35 | 36 | /** The Constant serialVersionUID. */ 37 | private static final long serialVersionUID = 1L; 38 | 39 | /** The Constant logger. */ 40 | private static final Logger logger = LoggerFactory.getLogger(XmlCompressorTag.class); 41 | 42 | /** The enabled. */ 43 | private boolean enabled = true; 44 | 45 | /** The remove comments. */ 46 | // default settings 47 | private boolean removeComments = true; 48 | 49 | /** The remove intertag spaces. */ 50 | private boolean removeIntertagSpaces = true; 51 | 52 | @Override 53 | public int doEndTag() throws JspException { 54 | 55 | BodyContent bodyContent = getBodyContent(); 56 | String content = bodyContent.getString(); 57 | 58 | XmlCompressor compressor = new XmlCompressor(); 59 | compressor.setEnabled(enabled); 60 | compressor.setRemoveComments(removeComments); 61 | compressor.setRemoveIntertagSpaces(removeIntertagSpaces); 62 | 63 | try { 64 | bodyContent.clear(); 65 | bodyContent.append(compressor.compress(content)); 66 | bodyContent.writeOut(pageContext.getOut()); 67 | } catch (IOException e) { 68 | logger.error("", e); 69 | } 70 | 71 | return super.doEndTag(); 72 | } 73 | 74 | /** 75 | * Sets the enabled. 76 | * 77 | * @param enabled 78 | * the new enabled 79 | * 80 | * @see XmlCompressor#setEnabled(boolean) 81 | */ 82 | public void setEnabled(boolean enabled) { 83 | this.enabled = enabled; 84 | } 85 | 86 | /** 87 | * Sets the removes the comments. 88 | * 89 | * @param removeComments 90 | * the new removes the comments 91 | * 92 | * @see XmlCompressor#setRemoveComments(boolean) 93 | */ 94 | public void setRemoveComments(boolean removeComments) { 95 | this.removeComments = removeComments; 96 | } 97 | 98 | /** 99 | * Sets the removes the intertag spaces. 100 | * 101 | * @param removeIntertagSpaces 102 | * the new removes the intertag spaces 103 | * 104 | * @see XmlCompressor#setRemoveIntertagSpaces(boolean) 105 | */ 106 | public void setRemoveIntertagSpaces(boolean removeIntertagSpaces) { 107 | this.removeIntertagSpaces = removeIntertagSpaces; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/velocity/CssCompressorDirective.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.velocity; 17 | 18 | import com.googlecode.htmlcompressor.compressor.HtmlCompressor; 19 | import com.googlecode.htmlcompressor.compressor.YuiCssCompressor; 20 | 21 | import java.io.IOException; 22 | import java.io.StringWriter; 23 | import java.io.Writer; 24 | 25 | import org.apache.velocity.context.InternalContextAdapter; 26 | import org.apache.velocity.exception.MethodInvocationException; 27 | import org.apache.velocity.exception.TemplateInitException; 28 | import org.apache.velocity.runtime.RuntimeServices; 29 | import org.apache.velocity.runtime.directive.Directive; 30 | import org.apache.velocity.runtime.parser.node.Node; 31 | 32 | /** 33 | * Velocity directive that compresses an CSS content within #compressCss ... #end block. All CSS-related properties from 34 | * {@link HtmlCompressor} are supported. 35 | * 36 | * @see HtmlCompressor 37 | * @see Yahoo YUI Compressor 38 | */ 39 | public class CssCompressorDirective extends Directive { 40 | 41 | /** The enabled. */ 42 | private boolean enabled = true; 43 | 44 | // YUICompressor settings 45 | 46 | /** The yui css line break. */ 47 | private int yuiCssLineBreak = -1; 48 | 49 | @Override 50 | public String getName() { 51 | return "compressCss"; 52 | } 53 | 54 | @Override 55 | public int getType() { 56 | return BLOCK; 57 | } 58 | 59 | @Override 60 | public void init(RuntimeServices rs, InternalContextAdapter context, Node node) throws TemplateInitException { 61 | super.init(rs, context, node); 62 | log = rs.getLog(); 63 | 64 | // set compressor properties 65 | enabled = rs.getBoolean("userdirective.compressCss.enabled", true); 66 | yuiCssLineBreak = rs.getInt("userdirective.compressCss.yuiCssLineBreak", -1); 67 | } 68 | 69 | @Override 70 | public boolean render(InternalContextAdapter context, Writer writer, Node node) 71 | throws IOException, MethodInvocationException { 72 | 73 | // render content 74 | StringWriter content = new StringWriter(); 75 | node.jjtGetChild(0).render(context, content); 76 | 77 | // compress 78 | if (enabled) { 79 | try { 80 | 81 | YuiCssCompressor compressor = new YuiCssCompressor(); 82 | compressor.setLineBreak(yuiCssLineBreak); 83 | String result = compressor.compress(content.toString()); 84 | 85 | writer.write(result); 86 | } catch (Exception e) { 87 | writer.write(content.toString()); 88 | String msg = "Failed to compress content: " + content.toString(); 89 | log.error(msg, e); 90 | throw new RuntimeException(msg, e); 91 | 92 | } 93 | } else { 94 | writer.write(content.toString()); 95 | } 96 | 97 | return true; 98 | 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.Authenticator; 23 | import java.net.PasswordAuthentication; 24 | import java.net.URI; 25 | import java.net.URL; 26 | import java.nio.file.Files; 27 | import java.nio.file.Path; 28 | import java.nio.file.StandardCopyOption; 29 | import java.util.concurrent.ThreadLocalRandom; 30 | 31 | public final class MavenWrapperDownloader { 32 | private static final String WRAPPER_VERSION = "3.3.4"; 33 | 34 | private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); 35 | 36 | public static void main(String[] args) { 37 | log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); 38 | 39 | if (args.length != 2) { 40 | System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); 41 | System.exit(1); 42 | } 43 | 44 | try { 45 | log(" - Downloader started"); 46 | final URL wrapperUrl = URI.create(args[0]).toURL(); 47 | final Path baseDir = Path.of(".").toAbsolutePath().normalize(); 48 | final Path wrapperJarPath = baseDir.resolve(args[1]).normalize(); 49 | if (!wrapperJarPath.startsWith(baseDir)) { 50 | throw new IOException("Invalid path: outside of allowed directory"); 51 | } 52 | downloadFileFromURL(wrapperUrl, wrapperJarPath); 53 | log("Done"); 54 | } catch (IOException e) { 55 | System.err.println("- Error downloading: " + e.getMessage()); 56 | if (VERBOSE) { 57 | e.printStackTrace(); 58 | } 59 | System.exit(1); 60 | } 61 | } 62 | 63 | private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) 64 | throws IOException { 65 | log(" - Downloading to: " + wrapperJarPath); 66 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 67 | final String username = System.getenv("MVNW_USERNAME"); 68 | final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 69 | Authenticator.setDefault(new Authenticator() { 70 | @Override 71 | protected PasswordAuthentication getPasswordAuthentication() { 72 | return new PasswordAuthentication(username, password); 73 | } 74 | }); 75 | } 76 | Path temp = wrapperJarPath 77 | .getParent() 78 | .resolve(wrapperJarPath.getFileName() + "." 79 | + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); 80 | try (InputStream inStream = wrapperUrl.openStream()) { 81 | Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); 82 | Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); 83 | } finally { 84 | Files.deleteIfExists(temp); 85 | } 86 | log(" - Downloader complete"); 87 | } 88 | 89 | private static void log(String msg) { 90 | if (VERBOSE) { 91 | System.out.println(msg); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/compressor/HtmlCompressorStatistics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.compressor; 17 | 18 | /** 19 | * Class that stores HTML compression statistics. 20 | * 21 | * @see HtmlCompressor#getStatistics() 22 | */ 23 | public class HtmlCompressorStatistics { 24 | 25 | /** The original metrics. */ 26 | private HtmlMetrics originalMetrics = new HtmlMetrics(); 27 | 28 | /** The compressed metrics. */ 29 | private HtmlMetrics compressedMetrics = new HtmlMetrics(); 30 | 31 | /** The time. */ 32 | private long time; 33 | 34 | /** The preserved size. */ 35 | private int preservedSize; 36 | 37 | /** 38 | * Returns metrics of an uncompressed document. 39 | * 40 | * @return metrics of an uncompressed document 41 | * 42 | * @see HtmlMetrics 43 | */ 44 | public HtmlMetrics getOriginalMetrics() { 45 | return originalMetrics; 46 | } 47 | 48 | /** 49 | * Sets the original metrics. 50 | * 51 | * @param originalMetrics 52 | * the originalMetrics to set 53 | */ 54 | public void setOriginalMetrics(HtmlMetrics originalMetrics) { 55 | this.originalMetrics = originalMetrics; 56 | } 57 | 58 | /** 59 | * Returns metrics of a compressed document. 60 | * 61 | * @return metrics of a compressed document 62 | * 63 | * @see HtmlMetrics 64 | */ 65 | public HtmlMetrics getCompressedMetrics() { 66 | return compressedMetrics; 67 | } 68 | 69 | /** 70 | * Sets the compressed metrics. 71 | * 72 | * @param compressedMetrics 73 | * the compressedMetrics to set 74 | */ 75 | public void setCompressedMetrics(HtmlMetrics compressedMetrics) { 76 | this.compressedMetrics = compressedMetrics; 77 | } 78 | 79 | /** 80 | * Returns total compression time. 81 | *

82 | * Please note that compression performance varies very significantly depending on whether it was a cold run or not 83 | * (specifics of Java VM), so for accurate real world results it is recommended to take measurements accordingly. 84 | * 85 | * @return the compression time, in milliseconds 86 | */ 87 | public long getTime() { 88 | return time; 89 | } 90 | 91 | /** 92 | * Sets the time. 93 | * 94 | * @param time 95 | * the time to set 96 | */ 97 | public void setTime(long time) { 98 | this.time = time; 99 | } 100 | 101 | /** 102 | * Returns total size of blocks that were skipped by the compressor (for example content inside 103 | * <pre> tags or inside <script> tags with disabled javascript compression). 104 | * 105 | * @return the total size of blocks that were skipped by the compressor, in bytes 106 | */ 107 | public int getPreservedSize() { 108 | return preservedSize; 109 | } 110 | 111 | /** 112 | * Sets the preserved size. 113 | * 114 | * @param preservedSize 115 | * the preservedSize to set 116 | */ 117 | public void setPreservedSize(int preservedSize) { 118 | this.preservedSize = preservedSize; 119 | } 120 | 121 | @Override 122 | public String toString() { 123 | return String.format("Time=%d, Preserved=%d, Original={%s}, Compressed={%s}", time, preservedSize, 124 | originalMetrics.toString(), compressedMetrics.toString()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/compressor/HtmlMetrics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.compressor; 17 | 18 | /** 19 | * Class that stores metrics about HTML documents. 20 | */ 21 | public class HtmlMetrics { 22 | 23 | /** The filesize. */ 24 | private int filesize; 25 | 26 | /** The empty chars. */ 27 | private int emptyChars; 28 | 29 | /** The inline script size. */ 30 | private int inlineScriptSize; 31 | 32 | /** The inline style size. */ 33 | private int inlineStyleSize; 34 | 35 | /** The inline event size. */ 36 | private int inlineEventSize; 37 | 38 | /** 39 | * Returns total filesize of a document. 40 | * 41 | * @return total filesize of a document, in bytes 42 | */ 43 | public int getFilesize() { 44 | return filesize; 45 | } 46 | 47 | /** 48 | * Sets the filesize. 49 | * 50 | * @param filesize 51 | * the filesize to set 52 | */ 53 | public void setFilesize(int filesize) { 54 | this.filesize = filesize; 55 | } 56 | 57 | /** 58 | * Returns number of empty characters (spaces, tabs, end of lines) in a document. 59 | * 60 | * @return number of empty characters in a document 61 | */ 62 | public int getEmptyChars() { 63 | return emptyChars; 64 | } 65 | 66 | /** 67 | * Sets the empty chars. 68 | * 69 | * @param emptyChars 70 | * the emptyChars to set 71 | */ 72 | public void setEmptyChars(int emptyChars) { 73 | this.emptyChars = emptyChars; 74 | } 75 | 76 | /** 77 | * Returns total size of inline <script> tags. 78 | * 79 | * @return total size of inline <script> tags, in bytes 80 | */ 81 | public int getInlineScriptSize() { 82 | return inlineScriptSize; 83 | } 84 | 85 | /** 86 | * Sets the inline script size. 87 | * 88 | * @param inlineScriptSize 89 | * the inlineScriptSize to set 90 | */ 91 | public void setInlineScriptSize(int inlineScriptSize) { 92 | this.inlineScriptSize = inlineScriptSize; 93 | } 94 | 95 | /** 96 | * Returns total size of inline <style> tags. 97 | * 98 | * @return total size of inline <style> tags, in bytes 99 | */ 100 | public int getInlineStyleSize() { 101 | return inlineStyleSize; 102 | } 103 | 104 | /** 105 | * Sets the inline style size. 106 | * 107 | * @param inlineStyleSize 108 | * the inlineStyleSize to set 109 | */ 110 | public void setInlineStyleSize(int inlineStyleSize) { 111 | this.inlineStyleSize = inlineStyleSize; 112 | } 113 | 114 | /** 115 | * Returns total size of inline event handlers (onclick, etc). 116 | * 117 | * @return total size of inline event handlers, in bytes 118 | */ 119 | public int getInlineEventSize() { 120 | return inlineEventSize; 121 | } 122 | 123 | /** 124 | * Sets the inline event size. 125 | * 126 | * @param inlineEventSize 127 | * the inlineEventSize to set 128 | */ 129 | public void setInlineEventSize(int inlineEventSize) { 130 | this.inlineEventSize = inlineEventSize; 131 | } 132 | 133 | @Override 134 | public String toString() { 135 | return String.format("Filesize=%d, Empty Chars=%d, Script Size=%d, Style Size=%d, Event Handler Size=%d", 136 | filesize, emptyChars, inlineScriptSize, inlineStyleSize, inlineEventSize); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/htmlcompressor/compressor/XmlCompressorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.compressor; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | 20 | import java.io.IOException; 21 | import java.io.Reader; 22 | import java.nio.charset.StandardCharsets; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | 26 | import org.junit.jupiter.api.Test; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | /** 31 | * The Class XmlCompressorTest. 32 | */ 33 | // Keep public for javadocs 34 | public class XmlCompressorTest { 35 | 36 | /** The Constant logger. */ 37 | private static final Logger logger = LoggerFactory.getLogger(XmlCompressorTest.class); 38 | 39 | /** The Constant resPath. */ 40 | private static final String resPath = "./src/test/resources/xml/"; 41 | 42 | /** 43 | * Test compress. 44 | * 45 | * @throws Exception 46 | * the exception 47 | */ 48 | @Test 49 | void testCompress() throws Exception { 50 | String source = readResource("testCompress.xml"); 51 | String result = readResource("testCompressResult.xml"); 52 | 53 | XmlCompressor compressor = new XmlCompressor(); 54 | 55 | assertEquals(result, compressor.compress(source) + System.lineSeparator()); 56 | } 57 | 58 | /** 59 | * Test enabled. 60 | * 61 | * @throws Exception 62 | * the exception 63 | */ 64 | @Test 65 | void testEnabled() throws Exception { 66 | String source = readResource("testEnabled.xml"); 67 | String result = readResource("testEnabledResult.xml"); 68 | 69 | XmlCompressor compressor = new XmlCompressor(); 70 | compressor.setEnabled(false); 71 | 72 | assertEquals(result, compressor.compress(source)); 73 | } 74 | 75 | /** 76 | * Test remove comments. 77 | * 78 | * @throws Exception 79 | * the exception 80 | */ 81 | @Test 82 | void testRemoveComments() throws Exception { 83 | String source = readResource("testRemoveComments.xml"); 84 | String result = readResource("testRemoveCommentsResult.xml"); 85 | 86 | XmlCompressor compressor = new XmlCompressor(); 87 | compressor.setRemoveComments(true); 88 | 89 | assertEquals(result, compressor.compress(source) + System.lineSeparator()); 90 | } 91 | 92 | /** 93 | * Test remove intertag spaces. 94 | * 95 | * @throws Exception 96 | * the exception 97 | */ 98 | @Test 99 | void testRemoveIntertagSpaces() throws Exception { 100 | String source = readResource("testRemoveIntertagSpaces.xml"); 101 | String result = readResource("testRemoveIntertagSpacesResult.xml"); 102 | 103 | XmlCompressor compressor = new XmlCompressor(); 104 | compressor.setRemoveIntertagSpaces(true); 105 | 106 | assertEquals(result, compressor.compress(source) + System.lineSeparator()); 107 | } 108 | 109 | /** 110 | * Read resource. 111 | * 112 | * @param filename 113 | * the filename 114 | * 115 | * @return the string 116 | */ 117 | private String readResource(String filename) { 118 | StringBuilder builder = new StringBuilder(); 119 | try (Reader reader = Files.newBufferedReader(Path.of(resPath + filename), StandardCharsets.UTF_8)) { 120 | 121 | char[] buffer = new char[8192]; 122 | int read; 123 | while ((read = reader.read(buffer, 0, buffer.length)) > 0) { 124 | builder.append(buffer, 0, read); 125 | } 126 | 127 | } catch (IOException e) { 128 | logger.error("", e); 129 | } 130 | 131 | return builder.toString(); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/velocity/JavaScriptCompressorDirective.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.velocity; 17 | 18 | import com.google.javascript.jscomp.CompilationLevel; 19 | import com.googlecode.htmlcompressor.compressor.ClosureJavaScriptCompressor; 20 | import com.googlecode.htmlcompressor.compressor.HtmlCompressor; 21 | import com.googlecode.htmlcompressor.compressor.YuiJavaScriptCompressor; 22 | 23 | import java.io.IOException; 24 | import java.io.StringWriter; 25 | import java.io.Writer; 26 | 27 | import org.apache.velocity.context.InternalContextAdapter; 28 | import org.apache.velocity.exception.MethodInvocationException; 29 | import org.apache.velocity.exception.TemplateInitException; 30 | import org.apache.velocity.runtime.RuntimeServices; 31 | import org.apache.velocity.runtime.directive.Directive; 32 | import org.apache.velocity.runtime.parser.node.Node; 33 | 34 | /** 35 | * Velocity directive that compresses an JavaScript content within #compressJs ... #end block. All JavaScript-related 36 | * properties from {@link HtmlCompressor} are supported. 37 | * 38 | * @see HtmlCompressor 39 | * @see Yahoo YUI Compressor 40 | * @see Google Closure Compiler 41 | */ 42 | public class JavaScriptCompressorDirective extends Directive { 43 | 44 | /** The enabled. */ 45 | private boolean enabled = true; 46 | 47 | /** The js compressor. */ 48 | private String jsCompressor = HtmlCompressor.JS_COMPRESSOR_YUI; 49 | 50 | // YUICompressor settings 51 | 52 | /** The yui js no munge. */ 53 | private boolean yuiJsNoMunge; 54 | 55 | /** The yui js preserve all semi colons. */ 56 | private boolean yuiJsPreserveAllSemiColons; 57 | 58 | /** The yui js disable optimizations. */ 59 | private boolean yuiJsDisableOptimizations; 60 | 61 | /** The yui js line break. */ 62 | private int yuiJsLineBreak = -1; 63 | 64 | // Closure compressor settings 65 | 66 | /** The closure opt level. */ 67 | private String closureOptLevel = ClosureJavaScriptCompressor.COMPILATION_LEVEL_SIMPLE; 68 | 69 | @Override 70 | public String getName() { 71 | return "compressJs"; 72 | } 73 | 74 | @Override 75 | public int getType() { 76 | return BLOCK; 77 | } 78 | 79 | @Override 80 | public void init(RuntimeServices rs, InternalContextAdapter context, Node node) throws TemplateInitException { 81 | super.init(rs, context, node); 82 | log = rs.getLog(); 83 | 84 | // set compressor properties 85 | enabled = rs.getBoolean("userdirective.compressJs.enabled", true); 86 | jsCompressor = rs.getString("userdirective.compressHtml.jsCompressor", HtmlCompressor.JS_COMPRESSOR_YUI); 87 | yuiJsNoMunge = rs.getBoolean("userdirective.compressJs.yuiJsNoMunge", false); 88 | yuiJsPreserveAllSemiColons = rs.getBoolean("userdirective.compressJs.yuiJsPreserveAllSemiColons", false); 89 | yuiJsLineBreak = rs.getInt("userdirective.compressJs.yuiJsLineBreak", -1); 90 | closureOptLevel = rs.getString("userdirective.compressHtml.closureOptLevel", 91 | ClosureJavaScriptCompressor.COMPILATION_LEVEL_SIMPLE); 92 | } 93 | 94 | @Override 95 | public boolean render(InternalContextAdapter context, Writer writer, Node node) 96 | throws IOException, MethodInvocationException { 97 | 98 | // render content 99 | StringWriter content = new StringWriter(); 100 | node.jjtGetChild(0).render(context, content); 101 | 102 | // compress 103 | if (enabled) { 104 | try { 105 | String result = content.toString(); 106 | 107 | if (jsCompressor.equalsIgnoreCase(HtmlCompressor.JS_COMPRESSOR_CLOSURE)) { 108 | // call Closure compressor 109 | ClosureJavaScriptCompressor closureCompressor = new ClosureJavaScriptCompressor(); 110 | if (closureOptLevel.equalsIgnoreCase(ClosureJavaScriptCompressor.COMPILATION_LEVEL_ADVANCED)) { 111 | closureCompressor.setCompilationLevel(CompilationLevel.ADVANCED_OPTIMIZATIONS); 112 | } else if (closureOptLevel 113 | .equalsIgnoreCase(ClosureJavaScriptCompressor.COMPILATION_LEVEL_WHITESPACE)) { 114 | closureCompressor.setCompilationLevel(CompilationLevel.WHITESPACE_ONLY); 115 | } else { 116 | closureCompressor.setCompilationLevel(CompilationLevel.SIMPLE_OPTIMIZATIONS); 117 | } 118 | 119 | result = closureCompressor.compress(result); 120 | 121 | } else { 122 | // call YUICompressor 123 | YuiJavaScriptCompressor yuiCompressor = new YuiJavaScriptCompressor(); 124 | yuiCompressor.setDisableOptimizations(yuiJsDisableOptimizations); 125 | yuiCompressor.setLineBreak(yuiJsLineBreak); 126 | yuiCompressor.setNoMunge(yuiJsNoMunge); 127 | yuiCompressor.setPreserveAllSemiColons(yuiJsPreserveAllSemiColons); 128 | 129 | result = yuiCompressor.compress(result); 130 | } 131 | 132 | writer.write(result); 133 | } catch (Exception e) { 134 | writer.write(content.toString()); 135 | String msg = "Failed to compress content: " + content.toString(); 136 | log.error(msg, e); 137 | throw new RuntimeException(msg, e); 138 | 139 | } 140 | } else { 141 | writer.write(content.toString()); 142 | } 143 | 144 | return true; 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/velocity/HtmlCompressorDirective.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.velocity; 17 | 18 | import com.google.javascript.jscomp.CompilationLevel; 19 | import com.googlecode.htmlcompressor.compressor.ClosureJavaScriptCompressor; 20 | import com.googlecode.htmlcompressor.compressor.HtmlCompressor; 21 | 22 | import java.io.IOException; 23 | import java.io.StringWriter; 24 | import java.io.Writer; 25 | 26 | import org.apache.velocity.context.InternalContextAdapter; 27 | import org.apache.velocity.exception.MethodInvocationException; 28 | import org.apache.velocity.exception.TemplateInitException; 29 | import org.apache.velocity.runtime.RuntimeServices; 30 | import org.apache.velocity.runtime.directive.Directive; 31 | import org.apache.velocity.runtime.parser.node.Node; 32 | 33 | /** 34 | * Velocity directive that compresses an HTML content within #compressHtml ... #end block. Compression parameters are 35 | * set by default (no JavaScript and CSS compression). 36 | * 37 | * @see HtmlCompressor 38 | */ 39 | public class HtmlCompressorDirective extends Directive { 40 | 41 | /** The Constant htmlCompressor. */ 42 | private static final HtmlCompressor htmlCompressor = new HtmlCompressor(); 43 | 44 | @Override 45 | public String getName() { 46 | return "compressHtml"; 47 | } 48 | 49 | @Override 50 | public int getType() { 51 | return BLOCK; 52 | } 53 | 54 | @Override 55 | public void init(RuntimeServices rs, InternalContextAdapter context, Node node) throws TemplateInitException { 56 | super.init(rs, context, node); 57 | log = rs.getLog(); 58 | 59 | boolean compressJavaScript = rs.getBoolean("userdirective.compressHtml.compressJavaScript", false); 60 | 61 | // set compressor properties 62 | htmlCompressor.setEnabled(rs.getBoolean("userdirective.compressHtml.enabled", true)); 63 | htmlCompressor.setRemoveComments(rs.getBoolean("userdirective.compressHtml.removeComments", true)); 64 | htmlCompressor.setRemoveMultiSpaces(rs.getBoolean("userdirective.compressHtml.removeMultiSpaces", true)); 65 | htmlCompressor.setRemoveIntertagSpaces(rs.getBoolean("userdirective.compressHtml.removeIntertagSpaces", false)); 66 | htmlCompressor.setRemoveQuotes(rs.getBoolean("userdirective.compressHtml.removeQuotes", false)); 67 | htmlCompressor.setPreserveLineBreaks(rs.getBoolean("userdirective.compressHtml.preserveLineBreaks", false)); 68 | htmlCompressor.setCompressJavaScript(compressJavaScript); 69 | htmlCompressor.setCompressCss(rs.getBoolean("userdirective.compressHtml.compressCss", false)); 70 | htmlCompressor.setYuiJsNoMunge(rs.getBoolean("userdirective.compressHtml.yuiJsNoMunge", false)); 71 | htmlCompressor.setYuiJsPreserveAllSemiColons( 72 | rs.getBoolean("userdirective.compressHtml.yuiJsPreserveAllSemiColons", false)); 73 | htmlCompressor.setYuiJsLineBreak(rs.getInt("userdirective.compressHtml.yuiJsLineBreak", -1)); 74 | htmlCompressor.setYuiCssLineBreak(rs.getInt("userdirective.compressHtml.yuiCssLineBreak", -1)); 75 | htmlCompressor.setSimpleDoctype(rs.getBoolean("userdirective.compressHtml.simpleDoctype", false)); 76 | htmlCompressor 77 | .setRemoveScriptAttributes(rs.getBoolean("userdirective.compressHtml.removeScriptAttributes", false)); 78 | htmlCompressor 79 | .setRemoveStyleAttributes(rs.getBoolean("userdirective.compressHtml.removeStyleAttributes", false)); 80 | htmlCompressor.setRemoveLinkAttributes(rs.getBoolean("userdirective.compressHtml.removeLinkAttributes", false)); 81 | htmlCompressor.setRemoveFormAttributes(rs.getBoolean("userdirective.compressHtml.removeFormAttributes", false)); 82 | htmlCompressor 83 | .setRemoveInputAttributes(rs.getBoolean("userdirective.compressHtml.removeInputAttributes", false)); 84 | htmlCompressor 85 | .setSimpleBooleanAttributes(rs.getBoolean("userdirective.compressHtml.simpleBooleanAttributes", false)); 86 | htmlCompressor.setRemoveJavaScriptProtocol( 87 | rs.getBoolean("userdirective.compressHtml.removeJavaScriptProtocol", false)); 88 | htmlCompressor.setRemoveHttpProtocol(rs.getBoolean("userdirective.compressHtml.removeHttpProtocol", false)); 89 | htmlCompressor.setRemoveHttpsProtocol(rs.getBoolean("userdirective.compressHtml.removeHttpsProtocol", false)); 90 | 91 | if (compressJavaScript 92 | && rs.getString("userdirective.compressHtml.jsCompressor", HtmlCompressor.JS_COMPRESSOR_YUI) 93 | .equalsIgnoreCase(HtmlCompressor.JS_COMPRESSOR_CLOSURE)) { 94 | String closureOptLevel = rs.getString("userdirective.compressHtml.closureOptLevel", 95 | ClosureJavaScriptCompressor.COMPILATION_LEVEL_SIMPLE); 96 | 97 | ClosureJavaScriptCompressor closureCompressor = new ClosureJavaScriptCompressor(); 98 | if (closureOptLevel.equalsIgnoreCase(ClosureJavaScriptCompressor.COMPILATION_LEVEL_ADVANCED)) { 99 | closureCompressor.setCompilationLevel(CompilationLevel.ADVANCED_OPTIMIZATIONS); 100 | } else if (closureOptLevel.equalsIgnoreCase(ClosureJavaScriptCompressor.COMPILATION_LEVEL_WHITESPACE)) { 101 | closureCompressor.setCompilationLevel(CompilationLevel.WHITESPACE_ONLY); 102 | } else { 103 | closureCompressor.setCompilationLevel(CompilationLevel.SIMPLE_OPTIMIZATIONS); 104 | } 105 | 106 | htmlCompressor.setJavaScriptCompressor(closureCompressor); 107 | } 108 | } 109 | 110 | @Override 111 | public boolean render(InternalContextAdapter context, Writer writer, Node node) 112 | throws IOException, MethodInvocationException { 113 | 114 | // render content 115 | StringWriter content = new StringWriter(); 116 | node.jjtGetChild(0).render(context, content); 117 | 118 | // compress 119 | try { 120 | writer.write(htmlCompressor.compress(content.toString())); 121 | } catch (Exception e) { 122 | writer.write(content.toString()); 123 | String msg = "Failed to compress content: " + content.toString(); 124 | log.error(msg, e); 125 | throw new RuntimeException(msg, e); 126 | 127 | } 128 | return true; 129 | 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/htmlcompressor.tld: -------------------------------------------------------------------------------- 1 | 2 | 19 | 23 | 1.1 24 | http://htmlcompressor.googlecode.com/taglib/compressor 25 | 26 | Compresses HTML content within tags. 27 | html 28 | com.googlecode.htmlcompressor.taglib.HtmlCompressorTag 29 | JSP 30 | 31 | enabled 32 | false 33 | true 34 | 35 | 36 | removeComments 37 | false 38 | true 39 | 40 | 41 | removeMultiSpaces 42 | false 43 | true 44 | 45 | 46 | removeIntertagSpaces 47 | false 48 | true 49 | 50 | 51 | removeQuotes 52 | false 53 | true 54 | 55 | 56 | compressJavaScript 57 | false 58 | true 59 | 60 | 61 | compressCss 62 | false 63 | true 64 | 65 | 66 | yuiJsNoMunge 67 | false 68 | true 69 | 70 | 71 | yuiJsPreserveAllSemiColons 72 | false 73 | true 74 | 75 | 76 | yuiJsDisableOptimizations 77 | false 78 | true 79 | 80 | 81 | yuiJsLineBreak 82 | false 83 | true 84 | 85 | 86 | yuiCssLineBreak 87 | false 88 | true 89 | 90 | 91 | jsCompressor 92 | false 93 | true 94 | 95 | 96 | closureOptLevel 97 | false 98 | true 99 | 100 | 101 | simpleDoctype 102 | false 103 | true 104 | 105 | 106 | removeScriptAttributes 107 | false 108 | true 109 | 110 | 111 | removeStyleAttributes 112 | false 113 | true 114 | 115 | 116 | removeLinkAttributes 117 | false 118 | true 119 | 120 | 121 | removeFormAttributes 122 | false 123 | true 124 | 125 | 126 | removeInputAttributes 127 | false 128 | true 129 | 130 | 131 | simpleBooleanAttributes 132 | false 133 | true 134 | 135 | 136 | removeJavaScriptProtocol 137 | false 138 | true 139 | 140 | 141 | removeHttpProtocol 142 | false 143 | true 144 | 145 | 146 | removeHttpsProtocol 147 | false 148 | true 149 | 150 | 151 | preserveLineBreaks 152 | false 153 | true 154 | 155 | 156 | 157 | Compresses XML content within tags. 158 | xml 159 | com.googlecode.htmlcompressor.taglib.XmlCompressorTag 160 | JSP 161 | 162 | enabled 163 | false 164 | true 165 | 166 | 167 | removeComments 168 | false 169 | true 170 | 171 | 172 | removeIntertagSpaces 173 | false 174 | true 175 | 176 | 177 | 178 | Compresses JavaScript content within tags. 179 | js 180 | com.googlecode.htmlcompressor.taglib.JavaScriptCompressorTag 181 | JSP 182 | 183 | enabled 184 | false 185 | true 186 | 187 | 188 | yuiJsNoMunge 189 | false 190 | true 191 | 192 | 193 | yuiJsPreserveAllSemiColons 194 | false 195 | true 196 | 197 | 198 | yuiJsDisableOptimizations 199 | false 200 | true 201 | 202 | 203 | yuiJsLineBreak 204 | false 205 | true 206 | 207 | 208 | jsCompressor 209 | false 210 | true 211 | 212 | 213 | closureOptLevel 214 | false 215 | true 216 | 217 | 218 | 219 | Compresses CSS content within tags. 220 | css 221 | com.googlecode.htmlcompressor.taglib.CssCompressorTag 222 | JSP 223 | 224 | enabled 225 | false 226 | true 227 | 228 | 229 | yuiCssLineBreak 230 | false 231 | true 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/taglib/JavaScriptCompressorTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.taglib; 17 | 18 | import com.google.javascript.jscomp.CompilationLevel; 19 | import com.googlecode.htmlcompressor.compressor.ClosureJavaScriptCompressor; 20 | import com.googlecode.htmlcompressor.compressor.HtmlCompressor; 21 | import com.googlecode.htmlcompressor.compressor.YuiJavaScriptCompressor; 22 | 23 | import jakarta.servlet.jsp.JspException; 24 | import jakarta.servlet.jsp.tagext.BodyContent; 25 | import jakarta.servlet.jsp.tagext.BodyTagSupport; 26 | 27 | import java.io.IOException; 28 | 29 | import org.slf4j.Logger; 30 | import org.slf4j.LoggerFactory; 31 | 32 | /** 33 | * JSP tag that compresses an JavaScript content within <compress:js> tags. All JavaScript-related properties from 34 | * {@link HtmlCompressor} are supported. 35 | * 36 | * @see HtmlCompressor 37 | * @see Yahoo YUI Compressor 38 | * @see Google Closure Compiler 39 | */ 40 | public class JavaScriptCompressorTag extends BodyTagSupport { 41 | 42 | /** The Constant serialVersionUID. */ 43 | private static final long serialVersionUID = 1L; 44 | 45 | /** The Constant logger. */ 46 | private static final Logger logger = LoggerFactory.getLogger(JavaScriptCompressorTag.class); 47 | 48 | /** The enabled. */ 49 | private boolean enabled = true; 50 | 51 | /** The js compressor. */ 52 | private String jsCompressor = HtmlCompressor.JS_COMPRESSOR_YUI; 53 | 54 | // YUICompressor settings 55 | 56 | /** The yui js no munge. */ 57 | private boolean yuiJsNoMunge; 58 | 59 | /** The yui js preserve all semi colons. */ 60 | private boolean yuiJsPreserveAllSemiColons; 61 | 62 | /** The yui js disable optimizations. */ 63 | private boolean yuiJsDisableOptimizations; 64 | 65 | /** The yui js line break. */ 66 | private int yuiJsLineBreak = -1; 67 | 68 | // Closure compressor settings 69 | 70 | /** The closure opt level. */ 71 | private String closureOptLevel = ClosureJavaScriptCompressor.COMPILATION_LEVEL_SIMPLE; 72 | 73 | @Override 74 | public int doEndTag() throws JspException { 75 | 76 | BodyContent bodyContent = getBodyContent(); 77 | String content = bodyContent.getString(); 78 | 79 | try { 80 | if (enabled) { 81 | 82 | String result; 83 | 84 | if (jsCompressor.equalsIgnoreCase(HtmlCompressor.JS_COMPRESSOR_CLOSURE)) { 85 | // call Closure compressor 86 | ClosureJavaScriptCompressor closureCompressor = new ClosureJavaScriptCompressor(); 87 | if (closureOptLevel.equalsIgnoreCase(ClosureJavaScriptCompressor.COMPILATION_LEVEL_ADVANCED)) { 88 | closureCompressor.setCompilationLevel(CompilationLevel.ADVANCED_OPTIMIZATIONS); 89 | } else if (closureOptLevel 90 | .equalsIgnoreCase(ClosureJavaScriptCompressor.COMPILATION_LEVEL_WHITESPACE)) { 91 | closureCompressor.setCompilationLevel(CompilationLevel.WHITESPACE_ONLY); 92 | } else { 93 | closureCompressor.setCompilationLevel(CompilationLevel.SIMPLE_OPTIMIZATIONS); 94 | } 95 | 96 | result = closureCompressor.compress(content); 97 | 98 | } else { 99 | // call YUICompressor 100 | YuiJavaScriptCompressor yuiCompressor = new YuiJavaScriptCompressor(); 101 | yuiCompressor.setDisableOptimizations(yuiJsDisableOptimizations); 102 | yuiCompressor.setLineBreak(yuiJsLineBreak); 103 | yuiCompressor.setNoMunge(yuiJsNoMunge); 104 | yuiCompressor.setPreserveAllSemiColons(yuiJsPreserveAllSemiColons); 105 | 106 | result = yuiCompressor.compress(content); 107 | } 108 | 109 | bodyContent.clear(); 110 | bodyContent.append(result); 111 | bodyContent.writeOut(pageContext.getOut()); 112 | } else { 113 | bodyContent.clear(); 114 | bodyContent.append(content); 115 | bodyContent.writeOut(pageContext.getOut()); 116 | } 117 | 118 | } catch (IOException e) { 119 | logger.error("", e); 120 | } 121 | 122 | return super.doEndTag(); 123 | } 124 | 125 | /** 126 | * Sets the yui js no munge. 127 | * 128 | * @param yuiJsNoMunge 129 | * the new yui js no munge 130 | * 131 | * @see HtmlCompressor#setYuiJsNoMunge(boolean) 132 | */ 133 | public void setYuiJsNoMunge(boolean yuiJsNoMunge) { 134 | this.yuiJsNoMunge = yuiJsNoMunge; 135 | } 136 | 137 | /** 138 | * Sets the yui js preserve all semi colons. 139 | * 140 | * @param yuiJsPreserveAllSemiColons 141 | * the new yui js preserve all semi colons 142 | * 143 | * @see HtmlCompressor#setYuiJsPreserveAllSemiColons(boolean) 144 | */ 145 | public void setYuiJsPreserveAllSemiColons(boolean yuiJsPreserveAllSemiColons) { 146 | this.yuiJsPreserveAllSemiColons = yuiJsPreserveAllSemiColons; 147 | } 148 | 149 | /** 150 | * Sets the yui js disable optimizations. 151 | * 152 | * @param yuiJsDisableOptimizations 153 | * the new yui js disable optimizations 154 | * 155 | * @see HtmlCompressor#setYuiJsDisableOptimizations(boolean) 156 | */ 157 | public void setYuiJsDisableOptimizations(boolean yuiJsDisableOptimizations) { 158 | this.yuiJsDisableOptimizations = yuiJsDisableOptimizations; 159 | } 160 | 161 | /** 162 | * Sets the yui js line break. 163 | * 164 | * @param yuiJsLineBreak 165 | * the new yui js line break 166 | * 167 | * @see HtmlCompressor#setYuiJsLineBreak(int) 168 | */ 169 | public void setYuiJsLineBreak(int yuiJsLineBreak) { 170 | this.yuiJsLineBreak = yuiJsLineBreak; 171 | } 172 | 173 | /** 174 | * Sets the enabled. 175 | * 176 | * @param enabled 177 | * the new enabled 178 | * 179 | * @see HtmlCompressor#setEnabled(boolean) 180 | */ 181 | public void setEnabled(boolean enabled) { 182 | this.enabled = enabled; 183 | } 184 | 185 | /** 186 | * Sets JavaScript compressor implementation that will be used to compress inline JavaScript in HTML. 187 | * 188 | * @param jsCompressor 189 | * Could be either "yui" for using {@link YuiJavaScriptCompressor} (used by default if none 190 | * provided) or "closure" for using {@link ClosureJavaScriptCompressor} 191 | * 192 | * @see YuiJavaScriptCompressor 193 | * @see ClosureJavaScriptCompressor 194 | * @see Yahoo YUI Compressor 195 | * @see Google Closure Compiler 196 | */ 197 | public void setJsCompressor(String jsCompressor) { 198 | this.jsCompressor = jsCompressor; 199 | } 200 | 201 | /** 202 | * Sets level of optimization if Google Closure Compiler is 203 | * used for compressing inline JavaScript. 204 | * 205 | * @param closureOptLevel 206 | * Could be either "simple" (used by default), "whitespace" or 207 | * "advanced" 208 | * 209 | * @see ClosureJavaScriptCompressor#setCompilationLevel(CompilationLevel) 210 | */ 211 | public void setClosureOptLevel(String closureOptLevel) { 212 | this.closureOptLevel = closureOptLevel; 213 | } 214 | 215 | } 216 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.3.4 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. >&2 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. >&2 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. >&2 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. >&2 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | 121 | @REM Maven main class is here to fix maven 4.0.0-beta-5 through 4.0.0-rc-4 122 | set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling 123 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 124 | 125 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" 126 | 127 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 128 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 129 | ) 130 | 131 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 132 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 133 | if exist %WRAPPER_JAR% ( 134 | if "%MVNW_VERBOSE%" == "true" ( 135 | echo Found %WRAPPER_JAR% 136 | ) 137 | ) else ( 138 | if not "%MVNW_REPOURL%" == "" ( 139 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" 140 | ) 141 | if "%MVNW_VERBOSE%" == "true" ( 142 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 143 | echo Downloading from: %WRAPPER_URL% 144 | ) 145 | 146 | powershell -Command "&{"^ 147 | "$webclient = new-object System.Net.WebClient;"^ 148 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 149 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 150 | "}"^ 151 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 152 | "}" 153 | if "%MVNW_VERBOSE%" == "true" ( 154 | echo Finished downloading %WRAPPER_JAR% 155 | ) 156 | ) 157 | @REM End of extension 158 | 159 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 160 | SET WRAPPER_SHA_256_SUM="" 161 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 162 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 163 | ) 164 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 165 | powershell -Command "&{"^ 166 | "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ 167 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 168 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 169 | " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 170 | " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 171 | " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 172 | " exit 1;"^ 173 | "}"^ 174 | "}" 175 | if ERRORLEVEL 1 goto error 176 | ) 177 | 178 | @REM Provide a "standardized" way to retrieve the CLI args that will 179 | @REM work with both Windows and non-Windows executions. 180 | set MAVEN_CMD_LINE_ARGS=%* 181 | 182 | %MAVEN_JAVA_EXE% ^ 183 | %JVM_CONFIG_MAVEN_PROPS% ^ 184 | %MAVEN_OPTS% ^ 185 | %MAVEN_DEBUG_OPTS% ^ 186 | -classpath %WRAPPER_JAR% ^ 187 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 188 | "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" ^ 189 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 190 | if ERRORLEVEL 1 goto error 191 | goto end 192 | 193 | :error 194 | set ERROR_CODE=1 195 | 196 | :end 197 | @endlocal & set ERROR_CODE=%ERROR_CODE% 198 | 199 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 200 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 201 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 202 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 203 | :skipRcPost 204 | 205 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 206 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 207 | 208 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 209 | 210 | cmd /C exit /B %ERROR_CODE% 211 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/compressor/XmlCompressor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.compressor; 17 | 18 | import java.text.MessageFormat; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.regex.Matcher; 22 | import java.util.regex.Pattern; 23 | 24 | /** 25 | * Class that compresses given XML source by removing comments, extra spaces and line breaks while preserving content 26 | * within CDATA blocks. 27 | */ 28 | public class XmlCompressor implements Compressor { 29 | 30 | /** The enabled. */ 31 | private boolean enabled = true; 32 | 33 | /** The remove comments. */ 34 | // default settings 35 | private boolean removeComments = true; 36 | 37 | /** The remove intertag spaces. */ 38 | private boolean removeIntertagSpaces = true; 39 | 40 | /** The Constant tempCdataBlock. */ 41 | // temp replacements for preserved blocks 42 | protected static final String TEMP_CD_DATA_BLOCK = "%%%COMPRESS~CDATA~{0,number,#}%%%"; 43 | 44 | /** The Constant cdataPattern. */ 45 | // compiled regex patterns 46 | protected static final Pattern cdataPattern = Pattern.compile("", 47 | Pattern.DOTALL | Pattern.CASE_INSENSITIVE); 48 | 49 | /** The Constant commentPattern. */ 50 | protected static final Pattern commentPattern = Pattern.compile("", 51 | Pattern.DOTALL | Pattern.CASE_INSENSITIVE); 52 | 53 | /** The Constant intertagPattern. */ 54 | protected static final Pattern intertagPattern = Pattern.compile(">\\s+<", 55 | Pattern.DOTALL | Pattern.CASE_INSENSITIVE); 56 | 57 | /** The Constant tagEndSpacePattern. */ 58 | protected static final Pattern tagEndSpacePattern = Pattern.compile("(<(?:[^>]+?))(?:\\s+?)(/?>)", 59 | Pattern.DOTALL | Pattern.CASE_INSENSITIVE); 60 | 61 | /** The Constant multispacePattern. */ 62 | protected static final Pattern multispacePattern = Pattern.compile("\\s+(?=[^<]*?>)", 63 | Pattern.DOTALL | Pattern.CASE_INSENSITIVE); 64 | 65 | /** The Constant tagPropertyPattern. */ 66 | protected static final Pattern tagPropertyPattern = Pattern.compile("(\\s\\w+)\\s*=\\s*(?=[^<]*?>)", 67 | Pattern.CASE_INSENSITIVE); 68 | 69 | /** The Constant tempCdataPattern. */ 70 | protected static final Pattern tempCdataPattern = Pattern.compile("%%%COMPRESS~CDATA~(\\d+?)%%%", 71 | Pattern.DOTALL | Pattern.CASE_INSENSITIVE); 72 | 73 | /** 74 | * The main method that compresses given XML source and returns compressed result. 75 | * 76 | * @param xml 77 | * XML content to compress 78 | * 79 | * @return compressed content. 80 | */ 81 | @Override 82 | public String compress(String xml) { 83 | if (!enabled || xml == null || xml.isEmpty()) { 84 | return xml; 85 | } 86 | 87 | // preserved block containers 88 | List cdataBlocks = new ArrayList<>(); 89 | 90 | // preserve blocks 91 | xml = preserveBlocks(xml, cdataBlocks); 92 | 93 | // process pure xml 94 | xml = processXml(xml); 95 | 96 | // return preserved blocks 97 | xml = returnBlocks(xml, cdataBlocks); 98 | 99 | return xml.trim(); 100 | } 101 | 102 | /** 103 | * Preserve blocks. 104 | * 105 | * @param xml 106 | * the xml 107 | * @param cdataBlocks 108 | * the cdata blocks 109 | * 110 | * @return the string 111 | */ 112 | protected String preserveBlocks(String xml, List cdataBlocks) { 113 | // preserve CDATA blocks 114 | Matcher matcher = cdataPattern.matcher(xml); 115 | int index = 0; 116 | StringBuilder sb = new StringBuilder(); 117 | while (matcher.find()) { 118 | cdataBlocks.add(matcher.group(0)); 119 | matcher.appendReplacement(sb, MessageFormat.format(TEMP_CD_DATA_BLOCK, index++)); 120 | } 121 | matcher.appendTail(sb); 122 | xml = sb.toString(); 123 | 124 | return xml; 125 | } 126 | 127 | /** 128 | * Return blocks. 129 | * 130 | * @param xml 131 | * the xml 132 | * @param cdataBlocks 133 | * the cdata blocks 134 | * 135 | * @return the string 136 | */ 137 | protected String returnBlocks(String xml, List cdataBlocks) { 138 | // put CDATA blocks back 139 | Matcher matcher = tempCdataPattern.matcher(xml); 140 | StringBuilder sb = new StringBuilder(); 141 | while (matcher.find()) { 142 | matcher.appendReplacement(sb, 143 | Matcher.quoteReplacement(cdataBlocks.get(Integer.parseInt(matcher.group(1))))); 144 | } 145 | matcher.appendTail(sb); 146 | xml = sb.toString(); 147 | 148 | return xml; 149 | } 150 | 151 | /** 152 | * Process xml. 153 | * 154 | * @param xml 155 | * the xml 156 | * 157 | * @return the string 158 | */ 159 | protected String processXml(String xml) { 160 | // remove comments 161 | xml = removeComments(xml); 162 | 163 | // remove inter-tag spaces 164 | xml = removeIntertagSpaces(xml); 165 | 166 | // remove unneeded spaces inside tags 167 | xml = removeSpacesInsideTags(xml); 168 | 169 | return xml; 170 | } 171 | 172 | /** 173 | * Removes the spaces inside tags. 174 | * 175 | * @param xml 176 | * the xml 177 | * 178 | * @return the string 179 | */ 180 | protected String removeSpacesInsideTags(String xml) { 181 | // replace miltiple spaces inside tags with single spaces 182 | xml = multispacePattern.matcher(xml).replaceAll(" "); 183 | 184 | // remove spaces around equal sign inside tags 185 | xml = tagPropertyPattern.matcher(xml).replaceAll("$1="); 186 | 187 | // remove ending spaces inside tags 188 | xml = tagEndSpacePattern.matcher(xml).replaceAll("$1$2"); 189 | return xml; 190 | } 191 | 192 | /** 193 | * Removes the intertag spaces. 194 | * 195 | * @param xml 196 | * the xml 197 | * 198 | * @return the string 199 | */ 200 | protected String removeIntertagSpaces(String xml) { 201 | // remove inter-tag spaces 202 | if (removeIntertagSpaces) { 203 | xml = intertagPattern.matcher(xml).replaceAll("><"); 204 | } 205 | return xml; 206 | } 207 | 208 | /** 209 | * Removes the comments. 210 | * 211 | * @param xml 212 | * the xml 213 | * 214 | * @return the string 215 | */ 216 | protected String removeComments(String xml) { 217 | // remove comments 218 | if (removeComments) { 219 | xml = commentPattern.matcher(xml).replaceAll(""); 220 | } 221 | return xml; 222 | } 223 | 224 | /** 225 | * Returns true if compression is enabled. 226 | * 227 | * @return true if compression is enabled. 228 | */ 229 | public boolean isEnabled() { 230 | return enabled; 231 | } 232 | 233 | /** 234 | * If set to false all compression will be bypassed. Might be useful for testing purposes. Default is 235 | * true. 236 | * 237 | * @param enabled 238 | * set false to bypass all compression 239 | */ 240 | public void setEnabled(boolean enabled) { 241 | this.enabled = enabled; 242 | } 243 | 244 | /** 245 | * Returns true if all XML comments will be removed. 246 | * 247 | * @return true if all XML comments will be removed 248 | */ 249 | public boolean isRemoveComments() { 250 | return removeComments; 251 | } 252 | 253 | /** 254 | * If set to true all XML comments will be removed. Default is true. 255 | * 256 | * @param removeComments 257 | * set true to remove all XML comments 258 | */ 259 | public void setRemoveComments(boolean removeComments) { 260 | this.removeComments = removeComments; 261 | } 262 | 263 | /** 264 | * Returns true if all inter-tag whitespace characters will be removed. 265 | * 266 | * @return true if all inter-tag whitespace characters will be removed. 267 | */ 268 | public boolean isRemoveIntertagSpaces() { 269 | return removeIntertagSpaces; 270 | } 271 | 272 | /** 273 | * If set to true all inter-tag whitespace characters will be removed. Default is true. 274 | * 275 | * @param removeIntertagSpaces 276 | * set true to remove all inter-tag whitespace characters 277 | */ 278 | public void setRemoveIntertagSpaces(boolean removeIntertagSpaces) { 279 | this.removeIntertagSpaces = removeIntertagSpaces; 280 | } 281 | 282 | } 283 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 4.0.0 22 | 23 | 24 | com.github.hazendaz 25 | base-parent 26 | 56 27 | 28 | 29 | 30 | com.github.hazendaz 31 | htmlcompressor 32 | 2.3.2-SNAPSHOT 33 | 34 | htmlcompressor 35 | HtmlCompressor is a small, fast and very easy to use Java library that minifies given HTML or XML source by 36 | removing extra whitespaces, comments and other unneeded characters without breaking the content structure. 37 | As a result pages become smaller in size and load faster. A command-line version of the compressor is also 38 | available. 39 | https://github.com/hazendaz/htmlcompressor/ 40 | 2009 41 | 42 | Jeremy Landis 43 | https://github.com/hazendaz/htmlcompressor/ 44 | 45 | 46 | 47 | 48 | Apache License 2.0 49 | http://www.apache.org/licenses/LICENSE-2.0 50 | repo 51 | htmlcompressor is distributed under Apache License 2.0 52 | 53 | 54 | 55 | 56 | 57 | serg472 58 | Sergiy Kovalchuk 59 | serg472@gmail.com 60 | http://code.google.com/p/htmlcompressor/ 61 | Sergiy Kovalchuk 62 | http://code.google.com/p/htmlcompressor/ 63 | 64 | owner 65 | developer 66 | 67 | -8 68 | 69 | 70 | 71 | 72 | Alex Tunyk 73 | alextunyk@gmail.com 74 | http://alex.tunyk.com 75 | TUNYK.COM 76 | http://tunyk.com 77 | 78 | developer 79 | 80 | +2 81 | 82 | 83 | 84 | 85 | scm:git:ssh://git@github.com/hazendaz/htmlcompressor.git 86 | scm:git:ssh://git@github.com/hazendaz/htmlcompressor.git 87 | HEAD 88 | https://github.com/hazendaz/htmlcompressor/ 89 | 90 | 91 | Github 92 | https://github.com/hazendaz/htmlcompressor/issues 93 | 94 | 95 | 96 | gh-pages-scm 97 | GitHub Pages 98 | scm:git:ssh://git@github.com/hazendaz/htmlcompressor.git 99 | 100 | 101 | 102 | 103 | 104 | 21 105 | 21 106 | 107 | 108 | 21 109 | true 110 | 111 | 112 | com.github.hazendaz.htmlcompressor 113 | 114 | 115 | 1764880248 116 | 117 | 118 | 2.0.17 119 | 120 | 121 | true 122 | true 123 | 124 | 125 | 126 | 127 | org.slf4j 128 | slf4j-api 129 | ${slf4j.version} 130 | 131 | 132 | org.slf4j 133 | slf4j-simple 134 | ${slf4j.version} 135 | true 136 | 137 | 138 | com.google.javascript 139 | closure-compiler 140 | v20250820 141 | true 142 | 143 | 144 | com.google.guava 145 | guava 146 | 33.5.0-jre 147 | true 148 | 149 | 150 | jakarta.servlet.jsp 151 | jakarta.servlet.jsp-api 152 | 4.0.0 153 | provided 154 | 155 | 156 | org.apache.velocity 157 | velocity-engine-core 158 | 2.4.1 159 | provided 160 | 161 | 162 | org.apache.commons 163 | commons-lang3 164 | 3.20.0 165 | provided 166 | 167 | 168 | com.yahoo.platform.yui 169 | yuicompressor 170 | 2.4.8 171 | true 172 | 173 | 174 | rhino 175 | js 176 | 177 | 178 | 179 | 180 | org.mozilla 181 | rhino 182 | 1.8.1 183 | true 184 | 185 | 186 | org.junit.jupiter 187 | junit-jupiter-engine 188 | 6.0.1 189 | test 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | com.mycila 198 | license-maven-plugin 199 | 200 | 201 | 202 | 203 | **/src/test/resources/html/* 204 | **/src/test/resources/xml/* 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | org.apache.maven.plugins 213 | maven-enforcer-plugin 214 | 215 | 216 | 217 | 218 | 219 | jakarta.servlet.jsp:jakarta.servlet.jsp-api 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | org.apache.maven.plugins 228 | maven-jar-plugin 229 | 230 | 231 | 232 | true 233 | com.googlecode.htmlcompressor.CmdLineCompressor 234 | 235 | 236 | 237 | 238 | 239 | 240 | org.apache.maven.plugins 241 | maven-assembly-plugin 242 | 243 | false 244 | 245 | ${project.basedir}/src/main/assembly/distributive.xml 246 | 247 | 248 | 249 | 250 | make-assembly 251 | 252 | single 253 | 254 | package 255 | 256 | 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/htmlcompressor/compressor/YuiJavaScriptCompressor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.googlecode.htmlcompressor.compressor; 17 | 18 | import com.yahoo.platform.yui.compressor.JavaScriptCompressor; 19 | 20 | import java.io.IOException; 21 | import java.io.StringReader; 22 | import java.io.StringWriter; 23 | 24 | import org.mozilla.javascript.ErrorReporter; 25 | import org.mozilla.javascript.EvaluatorException; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | /** 30 | * Basic JavaScript compressor implementation using Yahoo YUI 31 | * Compressor that could be used by {@link HtmlCompressor} for inline JavaScript compression. 32 | * 33 | * @see HtmlCompressor#setJavaScriptCompressor(Compressor) 34 | * @see Yahoo YUI Compressor 35 | */ 36 | public class YuiJavaScriptCompressor implements Compressor { 37 | 38 | /** The Constant logger. */ 39 | private static final Logger logger = LoggerFactory.getLogger(YuiJavaScriptCompressor.class); 40 | 41 | // YUICompressor default settings 42 | 43 | /** The no munge. */ 44 | private boolean noMunge; 45 | 46 | /** The preserve all semi colons. */ 47 | private boolean preserveAllSemiColons; 48 | 49 | /** The disable optimizations. */ 50 | private boolean disableOptimizations; 51 | 52 | /** The line break. */ 53 | private int lineBreak = -1; 54 | 55 | /** The error reporter. */ 56 | private ErrorReporter errorReporter = new DefaultErrorReporter(); 57 | 58 | @Override 59 | public String compress(String source) { 60 | 61 | StringWriter result = new StringWriter(); 62 | try { 63 | JavaScriptCompressor compressor = new JavaScriptCompressor(new StringReader(source), errorReporter); 64 | compressor.compress(result, lineBreak, !noMunge, false, preserveAllSemiColons, disableOptimizations); 65 | } catch (IOException e) { 66 | result.write(source); 67 | logger.error("", e); 68 | } 69 | return result.toString(); 70 | 71 | } 72 | 73 | /** 74 | * Default ErrorReporter implementation that uses System.err stream for error reporting. 75 | * Used by YUI Compressor to log errors during JavaScript compression. 76 | * 77 | * @see Yahoo YUI Compressor 78 | * @see ErrorReporter 79 | * Interface 80 | */ 81 | public static class DefaultErrorReporter implements ErrorReporter { 82 | 83 | @Override 84 | public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { 85 | if (line < 0) { 86 | logger.error("[WARNING] HtmlCompressor: '{}' during JavaScript compression", message); 87 | } else { 88 | logger.error("[WARNING] HtmlCompressor: '{}' at line [{}:{}] during JavaScript compression {}", message, 89 | line, lineOffset, lineSource != null ? ": " + lineSource : ""); 90 | } 91 | } 92 | 93 | @Override 94 | public void error(String message, String sourceName, int line, String lineSource, int lineOffset) { 95 | if (line < 0) { 96 | logger.error("[ERROR] HtmlCompressor: '{}' during JavaScript compression", message); 97 | } else { 98 | logger.error("[ERROR] HtmlCompressor: '{}' at line [{}:{}] during JavaScript compression {}", message, 99 | line, lineOffset, lineSource != null ? ": " + lineSource : ""); 100 | } 101 | } 102 | 103 | @Override 104 | public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, 105 | int lineOffset) { 106 | error(message, sourceName, line, lineSource, lineOffset); 107 | return new EvaluatorException(message); 108 | } 109 | } 110 | 111 | /** 112 | * Returns true if Yahoo YUI Compressor will only minify javascript without obfuscating local symbols. 113 | * This corresponds to --nomunge command line option. 114 | * 115 | * @return nomunge parameter value used for JavaScript compression. 116 | * 117 | * @see Yahoo YUI Compressor 118 | */ 119 | public boolean isNoMunge() { 120 | return noMunge; 121 | } 122 | 123 | /** 124 | * Tells Yahoo YUI Compressor to only minify javascript without obfuscating local symbols. This corresponds to 125 | * --nomunge command line option. This option has effect only if JavaScript compression is enabled. 126 | * Default is false. 127 | * 128 | * @param noMunge 129 | * set true to enable nomunge mode 130 | * 131 | * @see Yahoo YUI Compressor 132 | */ 133 | public void setNoMunge(boolean noMunge) { 134 | this.noMunge = noMunge; 135 | } 136 | 137 | /** 138 | * Returns true if Yahoo YUI Compressor will preserve unnecessary semicolons during JavaScript 139 | * compression. This corresponds to --preserve-semi command line option. 140 | * 141 | * @return preserve-semi parameter value used for JavaScript compression. 142 | * 143 | * @see Yahoo YUI Compressor 144 | */ 145 | public boolean isPreserveAllSemiColons() { 146 | return preserveAllSemiColons; 147 | } 148 | 149 | /** 150 | * Tells Yahoo YUI Compressor to preserve unnecessary semicolons during JavaScript compression. This corresponds to 151 | * --preserve-semi command line option. This option has effect only if JavaScript compression is 152 | * enabled. Default is false. 153 | * 154 | * @param preserveAllSemiColons 155 | * set true to enable preserve-semi mode 156 | * 157 | * @see Yahoo YUI Compressor 158 | */ 159 | public void setPreserveAllSemiColons(boolean preserveAllSemiColons) { 160 | this.preserveAllSemiColons = preserveAllSemiColons; 161 | } 162 | 163 | /** 164 | * Returns true if Yahoo YUI Compressor will disable all the built-in micro optimizations during 165 | * JavaScript compression. This corresponds to --disable-optimizations command line option. 166 | * 167 | * @return disable-optimizations parameter value used for JavaScript compression. 168 | * 169 | * @see Yahoo YUI Compressor 170 | */ 171 | public boolean isDisableOptimizations() { 172 | return disableOptimizations; 173 | } 174 | 175 | /** 176 | * Tells Yahoo YUI Compressor to disable all the built-in micro optimizations during JavaScript compression. This 177 | * corresponds to --disable-optimizations command line option. This option has effect only if 178 | * JavaScript compression is enabled. Default is false. 179 | * 180 | * @param disableOptimizations 181 | * set true to enable disable-optimizations mode 182 | * 183 | * @see Yahoo YUI Compressor 184 | */ 185 | public void setDisableOptimizations(boolean disableOptimizations) { 186 | this.disableOptimizations = disableOptimizations; 187 | } 188 | 189 | /** 190 | * Returns number of symbols per line Yahoo YUI Compressor will use during JavaScript compression. This corresponds 191 | * to --line-break command line option. 192 | * 193 | * @return line-break parameter value used for JavaScript compression. 194 | * 195 | * @see Yahoo YUI Compressor 196 | */ 197 | public int getLineBreak() { 198 | return lineBreak; 199 | } 200 | 201 | /** 202 | * Tells Yahoo YUI Compressor to break lines after the specified number of symbols during JavaScript compression. 203 | * This corresponds to --line-break command line option. This option has effect only if JavaScript 204 | * compression is enabled. Default is -1 to disable line breaks. 205 | * 206 | * @param lineBreak 207 | * set number of symbols per line 208 | * 209 | * @see Yahoo YUI Compressor 210 | */ 211 | public void setLineBreak(int lineBreak) { 212 | this.lineBreak = lineBreak; 213 | } 214 | 215 | /** 216 | * Returns ErrorReporter used by YUI Compressor to log error messages during JavasSript compression. 217 | * 218 | * @return ErrorReporter used by YUI Compressor to log error messages during JavasSript compression 219 | * 220 | * @see Yahoo YUI Compressor 221 | * @see Error Reporter 222 | * Interface 223 | */ 224 | public ErrorReporter getErrorReporter() { 225 | return errorReporter; 226 | } 227 | 228 | /** 229 | * Sets ErrorReporter that YUI Compressor will use for reporting errors during JavaScript compression. 230 | * If no ErrorReporter was provided {@link YuiJavaScriptCompressor.DefaultErrorReporter} will be used 231 | * which reports errors to System.err stream. 232 | * 233 | * @param errorReporter 234 | * ErrorReporter that will be used by YUI Compressor 235 | * 236 | * @see YuiJavaScriptCompressor.DefaultErrorReporter 237 | * @see Yahoo YUI Compressor 238 | * @see ErrorReporter 239 | * Interface 240 | */ 241 | public void setErrorReporter(ErrorReporter errorReporter) { 242 | this.errorReporter = errorReporter; 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.5.3 (March 6, 2012) 2 | -