diff --git a/Platform/Apple/ePub3.xcodeproj/project.pbxproj b/Platform/Apple/ePub3.xcodeproj/project.pbxproj index ba89ba550..4a477243b 100644 --- a/Platform/Apple/ePub3.xcodeproj/project.pbxproj +++ b/Platform/Apple/ePub3.xcodeproj/project.pbxproj @@ -19,6 +19,9 @@ 588D24201A02EF8F006A92BB /* PassThroughFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 588D241E1A02EF8F006A92BB /* PassThroughFilter.cpp */; }; 588D24211A02EF8F006A92BB /* PassThroughFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 588D241E1A02EF8F006A92BB /* PassThroughFilter.cpp */; }; 588D24221A02EF8F006A92BB /* PassThroughFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 588D241F1A02EF8F006A92BB /* PassThroughFilter.h */; }; + 59753FDB1C88972600EE7908 /* css_preprocessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59753FD91C88972600EE7908 /* css_preprocessor.cpp */; }; + 59753FDC1C88972600EE7908 /* css_preprocessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 59753FDA1C88972600EE7908 /* css_preprocessor.h */; }; + 59D714EA1C8E1FCD00F6CBDA /* css_preprocessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59753FD91C88972600EE7908 /* css_preprocessor.cpp */; }; 834B09011A0BD015006AEB12 /* filter_chain_byte_stream_range.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A250D33BAFB8E8EAEF5FE5B5 /* filter_chain_byte_stream_range.cpp */; }; 834B09021A0BD018006AEB12 /* filter_chain_byte_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A250DFDBD90C7E9C632B1E00 /* filter_chain_byte_stream.cpp */; }; 850B1AE916A75AC600619C3C /* TestData in CopyFiles */ = {isa = PBXBuildFile; fileRef = 850B1AE816A75AB000619C3C /* TestData */; }; @@ -447,6 +450,8 @@ 1EFA3ACA17AB0BEF003A4BC2 /* filter_manager_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filter_manager_impl.cpp; sourceTree = ""; }; 588D241E1A02EF8F006A92BB /* PassThroughFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PassThroughFilter.cpp; sourceTree = ""; }; 588D241F1A02EF8F006A92BB /* PassThroughFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PassThroughFilter.h; sourceTree = ""; }; + 59753FD91C88972600EE7908 /* css_preprocessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = css_preprocessor.cpp; sourceTree = ""; }; + 59753FDA1C88972600EE7908 /* css_preprocessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = css_preprocessor.h; sourceTree = ""; }; 850B1AE816A75AB000619C3C /* TestData */ = {isa = PBXFileReference; lastKnownFileType = folder; name = TestData; path = ../../TestData; sourceTree = ""; }; A250D0D74FF2AD1AB643D2FA /* media-overlays_smil_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "media-overlays_smil_data.h"; sourceTree = ""; }; A250D119DCB804938466E3D6 /* media-overlays_smil_model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "media-overlays_smil_model.cpp"; sourceTree = ""; }; @@ -1131,6 +1136,8 @@ AB95448216BAD32000EFD2FD /* switch_preprocessor.h */, AB95448616BAF11000EFD2FD /* object_preprocessor.cpp */, AB95448716BAF11000EFD2FD /* object_preprocessor.h */, + 59753FD91C88972600EE7908 /* css_preprocessor.cpp */, + 59753FDA1C88972600EE7908 /* css_preprocessor.h */, ); name = "Content Preprocessing"; sourceTree = ""; @@ -1837,6 +1844,7 @@ ABA4BA5D16A7414000161B77 /* logging.h in Headers */, ABA4BA5F16A7414000161B77 /* scoped_ptr.h in Headers */, ABA4BA6316A7414000161B77 /* string16.h in Headers */, + 59753FDC1C88972600EE7908 /* css_preprocessor.h in Headers */, ABA4BA6716A7414000161B77 /* gurl.h in Headers */, ABA4BA6D16A7414000161B77 /* url_canon.h in Headers */, ABA4BA7916A7414000161B77 /* url_canon_icu.h in Headers */, @@ -2029,6 +2037,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 59D714EA1C8E1FCD00F6CBDA /* css_preprocessor.cpp in Sources */, ABA4BAEF16ADF64400161B77 /* string16.cc in Sources */, ABA4BAF016ADF64400161B77 /* gurl.cc in Sources */, AB5284D417CBC395003D7BBF /* CPUCacheUtils.c in Sources */, @@ -2305,6 +2314,7 @@ 1ED0083B17A9B7F800819EBD /* byte_buffer.cpp in Sources */, 1ED0084417A9D6E800819EBD /* filter_manager.cpp in Sources */, A250D0731D530005B0FFF5D5 /* media-overlays_smil_model.cpp in Sources */, + 59753FDB1C88972600EE7908 /* css_preprocessor.cpp in Sources */, A250DCD0523C05493141CE3B /* media-overlays_smil_data.cpp in Sources */, 1ED0084817A9DC6F00819EBD /* initialization.cpp in Sources */, 1EFA3ACB17AB0BEF003A4BC2 /* filter_manager_impl.cpp in Sources */, diff --git a/ePub3/ePub/css_preprocessor.cpp b/ePub3/ePub/css_preprocessor.cpp new file mode 100644 index 000000000..29f019acf --- /dev/null +++ b/ePub3/ePub/css_preprocessor.cpp @@ -0,0 +1,157 @@ +// +// css_preprocessor.cpp +// ePub3 +// +// Created by Olivier Körner on 2016-03-08. +// Copyright (c) 2016 Readium Foundation and/or its licensees. All rights reserved. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// +// Licensed under Gnu Affero General Public License Version 3 (provided, notwithstanding this notice, +// Readium Foundation reserves the right to license this material under a different separate license, +// and if you have done so, the terms of that separate license control and the following references +// to GPL do not apply). +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU +// Affero General Public License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. You should have received a copy of the GNU +// Affero General Public License along with this program. If not, see . + +#include "css_preprocessor.h" +#include "package.h" +#include "filter_manager.h" + +static const REGEX_NS::regex::flag_type regexFlags(REGEX_NS::regex::ECMAScript|REGEX_NS::regex::optimize|REGEX_NS::regex::icase); + +EPUB3_BEGIN_NAMESPACE + +static REGEX_NS::regex CSSMatcher("page\\-break\\-(after|before|inside) *\\: *(always|avoid|left|right)", regexFlags); +static REGEX_NS::regex StyleAttributeMatcher("<[^>]+style=\\\"([^\\\"]*)\"", regexFlags); +static REGEX_NS::regex StyleTagMatcher("]*>((.|\n|\r)*?)<\\/style>", regexFlags); + +static const std::string PageBreakReplacement = "-webkit-column-break-$1: $2; column-break-$&: $2"; + + +bool CSSPreprocessor::ShouldApply(ConstManifestItemPtr item) +{ + auto mediaType = item->MediaType(); + bool itemPrepaginated = false; + auto iprop = item->PropertyMatching("layout", "rendition"); + if (iprop != nullptr) { + auto ilayout = iprop->Value(); + itemPrepaginated = (ilayout == "pre-paginated"); + } + bool pkgPrepaginated = false; + auto prop = item->GetPackage()->PropertyMatching("layout", "rendition"); + if (prop != nullptr) { + auto layout = prop->Value(); + pkgPrepaginated = (layout == "pre-paginated"); + } + + if (itemPrepaginated || pkgPrepaginated) + return false; + + return (mediaType == "application/xhtml+xml" || mediaType == "text/html" || mediaType == "text/css"); +} + +ContentFilterPtr CSSPreprocessor::CSSFilterFactory(ConstPackagePtr package) +{ + CSSSubstitution pageBreakSub(CSSMatcher, PageBreakReplacement); + std::vector substitutions { pageBreakSub }; + return New(package, substitutions); +} + +void CSSPreprocessor::Register() +{ + FilterManager::Instance()->RegisterFilter("CSSPreprocessor", ValidationComplete, CSSFilterFactory); +} + +CSSPreprocessor::CSSPreprocessor(ConstPackagePtr pkg, CSSSubstitutionList substitutions) : ContentFilter(ShouldApply), m_substitutions(substitutions) +{ +} + + +void* CSSPreprocessor::FilterData(FilterContext* context, void *data, size_t len, size_t *outputLen) +{ + CSSFilterContext* p = dynamic_cast(context); + bool isCSS = p->isCSS(); + char* input = reinterpret_cast(data); + + std::string output; + if (isCSS) { + // Data is pure CSS, proceed with substitutions + output.assign(input, len); + for (CSSSubstitution& substitution: m_substitutions) { + output = REGEX_NS::regex_replace(output, substitution.GetSearchRegex(), substitution.GetReplaceFormat()); + } + } + else + { + // Data is HTML, look for