diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index e2e23db1b52a..cfc65273f914 100644 --- a/community-resources/src/idea/IdeaApplicationInfo.xml +++ b/community-resources/src/idea/IdeaApplicationInfo.xml @@ -4,7 +4,7 @@ - + diff --git a/community-resources/src/idea_community_about.png b/community-resources/src/idea_community_about.png index 7e9e496d6d21..a474783912da 100644 Binary files a/community-resources/src/idea_community_about.png and b/community-resources/src/idea_community_about.png differ diff --git a/community-resources/src/idea_community_about@2x.png b/community-resources/src/idea_community_about@2x.png index b5553ec2a21e..c8e0fadae88d 100644 Binary files a/community-resources/src/idea_community_about@2x.png and b/community-resources/src/idea_community_about@2x.png differ diff --git a/community-resources/src/idea_community_logo.png b/community-resources/src/idea_community_logo.png index 8a5a6785e534..ff26535f7394 100644 Binary files a/community-resources/src/idea_community_logo.png and b/community-resources/src/idea_community_logo.png differ diff --git a/community-resources/src/idea_community_logo@2x.png b/community-resources/src/idea_community_logo@2x.png index c20626941378..74457686ab1d 100644 Binary files a/community-resources/src/idea_community_logo@2x.png and b/community-resources/src/idea_community_logo@2x.png differ diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java index e6991736c0e3..192a64cf8d29 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java @@ -28,6 +28,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -206,7 +207,7 @@ public abstract class CopyPasteReferenceProcessor exten for (int i = 0; i < referenceData.length; i++) { PsiElement ref = refs[i]; if (ref != null) { - LOG.assertTrue(ref.isValid()); + PsiUtilCore.ensureValid(ref); Object refObject = refObjects[i]; boolean found = false; for (Object selected : selectedObjects) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java index b5ca2ea117c3..b0ae2ef73af8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java @@ -49,7 +49,7 @@ public class JavaTreeGenerator implements TreeGenerator { @Override @Nullable - public TreeElement generateTreeFor(PsiElement original, final CharTable table, final PsiManager manager) { + public TreeElement generateTreeFor(@NotNull PsiElement original, @NotNull final CharTable table, @NotNull final PsiManager manager) { if (original instanceof PsiKeyword || original instanceof PsiIdentifier) { final String text = original.getText(); return createLeafFromText(text, table, manager, original, ((PsiJavaToken)original).getTokenType()); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/typing/ClearingHighlightersOnType.xml b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/typing/ClearingHighlightersOnType.xml deleted file mode 100644 index 067abae0829e..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/typing/ClearingHighlightersOnType.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - rtetre tretre - rrr aksjdb sadkj fgrrretyr re tre tre tre t - - - - - - - - - diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java index fefe621beba1..d49eae4f0367 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java @@ -604,21 +604,6 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { assertEquals(3, markers.size()); } - - public void testClearingHighlightersOnType() throws Exception { - configureByFile(BASE_PATH + getTestName(false) + ".xml"); - - Collection errors = highlightErrors(); - assertEquals(1, errors.size()); - assertEquals("Element dddddddddd is not allowed here", errors.iterator().next().getDescription()); - - commentLine(); - - List afterTyping = DaemonCodeAnalyzerImpl.getHighlights(getEditor().getDocument(), HighlightSeverity.ERROR, getProject()); - HighlightInfo error = assertOneElement(afterTyping); - assertEquals("Element dddddddddd is not allowed here", error.getDescription()); - } - private static void commentLine() { WriteCommandAction.runWriteCommandAction(null, () -> { CommentByLineCommentAction action = new CommentByLineCommentAction(); @@ -628,7 +613,6 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { public void testChangeXmlIncludeLeadsToRehighlight() throws Exception { - LanguageFilter[] extensions = ((CompositeLanguage)StdLanguages.XML).getLanguageExtensions(); for (LanguageFilter extension : extensions) { ((CompositeLanguage)StdLanguages.XML).unregisterLanguageExtension(extension); diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarFileSystem.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarFileSystem.java index a106c2a0f277..312a1edf4d3b 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarFileSystem.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarFileSystem.java @@ -19,17 +19,24 @@ import com.intellij.openapi.util.Couple; import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ConcurrentFactoryMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.HashMap; import java.util.Map; /** * @author yole */ public class CoreJarFileSystem extends DeprecatedVirtualFileSystem { - private final Map myHandlers = new HashMap(); + private final Map myHandlers = new ConcurrentFactoryMap() { + @Nullable + @Override + protected CoreJarHandler create(String key) { + return new CoreJarHandler(CoreJarFileSystem.this, key); + } + }; @NotNull @Override @@ -40,7 +47,7 @@ public class CoreJarFileSystem extends DeprecatedVirtualFileSystem { @Override public VirtualFile findFileByPath(@NotNull @NonNls String path) { Couple pair = splitPath(path); - return getHandler(pair.first).findFileByPath(pair.second); + return myHandlers.get(pair.first).findFileByPath(pair.second); } @NotNull @@ -54,16 +61,6 @@ public class CoreJarFileSystem extends DeprecatedVirtualFileSystem { return Couple.of(localPath, pathInJar); } - @NotNull - private CoreJarHandler getHandler(String localPath) { - CoreJarHandler handler = myHandlers.get(localPath); - if (handler == null) { - handler = new CoreJarHandler(this, localPath); - myHandlers.put(localPath, handler); - } - return handler; - } - @Override public void refresh(boolean asynchronous) { } @@ -71,4 +68,9 @@ public class CoreJarFileSystem extends DeprecatedVirtualFileSystem { public VirtualFile refreshAndFindFileByPath(@NotNull String path) { return findFileByPath(path); } + + @SuppressWarnings("unused") // used in Kotlin + public void clearHandlersCache() { + myHandlers.clear(); + } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/ChangeUtil.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/ChangeUtil.java index 56e01ca4fa8d..13ca96e9df26 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/ChangeUtil.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/ChangeUtil.java @@ -144,7 +144,8 @@ public class ChangeUtil { } @Nullable - public static TreeElement generateTreeElement(PsiElement original, CharTable table, final PsiManager manager) { + public static TreeElement generateTreeElement(@Nullable PsiElement original, @NotNull CharTable table, @NotNull final PsiManager manager) { + if (original == null) return null; PsiUtilCore.ensureValid(original); if (SourceTreeToPsiMap.hasTreeElement(original)) { return copyElement((TreeElement)SourceTreeToPsiMap.psiElementToTree(original), table); diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeGenerator.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeGenerator.java index c6528ba7aeb3..20f27f3d5a39 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeGenerator.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeGenerator.java @@ -23,11 +23,12 @@ import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; import com.intellij.util.CharTable; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface TreeGenerator { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.treeGenerator"); @Nullable - TreeElement generateTreeFor(PsiElement original, CharTable table, final PsiManager manager); + TreeElement generateTreeFor(@NotNull PsiElement original, @NotNull CharTable table, @NotNull final PsiManager manager); } \ No newline at end of file diff --git a/platform/core-impl/src/com/intellij/util/DebugReflectionUtil.java b/platform/core-impl/src/com/intellij/util/DebugReflectionUtil.java index 21f93312a751..6d071a885e6c 100644 --- a/platform/core-impl/src/com/intellij/util/DebugReflectionUtil.java +++ b/platform/core-impl/src/com/intellij/util/DebugReflectionUtil.java @@ -127,7 +127,7 @@ public class DebugReflectionUtil { return true; } - private static boolean isLoadedAlready(Class root) { + private static boolean isLoadedAlready(@NotNull Class root) { if (Unsafe_shouldBeInitialized == null) return false; boolean isLoadedAlready = false; try { @@ -140,7 +140,7 @@ public class DebugReflectionUtil { } public static class BackLink { - public final Object value; + @NotNull public final Object value; private final Field field; private final BackLink backLink; @@ -158,15 +158,17 @@ public class DebugReflectionUtil { String valueStr; try { valueStr = backLink.value instanceof FList - ? "FList" : backLink.value instanceof Collection ? "Collection" : String.valueOf(backLink.value); + ? "FList (size="+((FList)backLink.value).size()+")" : + backLink.value instanceof Collection ? "Collection (size="+((Collection)backLink.value).size()+")" : + String.valueOf(backLink.value); valueStr = StringUtil.trimLog(StringUtil.convertLineSeparators(valueStr, "//"), 100); } catch (Throwable e) { valueStr = "(" + e.getMessage() + " while computing .toString())"; } Field field = backLink.field; - String fieldStr = field == null ? "?" : field.getName() + " of " + field.getDeclaringClass(); - result += "via " + fieldStr + "; Value: " + valueStr + " of " + backLink.value.getClass() + "\n"; + String fieldName = field == null ? "?" : field.getDeclaringClass().getName()+"."+field.getName(); + result += "via '" + fieldName + "'; Value: '" + valueStr + "' of " + backLink.value.getClass() + "\n"; backLink = backLink.backLink; } return result; diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java index d6385862296c..4b9bff7c64a9 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java @@ -42,7 +42,11 @@ public abstract class ListenerDiffViewerBase extends DiffViewerBase { super(context, request); myDocumentListener = createDocumentListener(); myFileListener = createFileListener(request); + } + @Override + protected void onInit() { + super.onInit(); if (myFileListener != null) VirtualFileManager.getInstance().addVirtualFileListener(myFileListener); for (Document document : getDocuments()) { diff --git a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeAction.java b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeAction.java index 60dbb83474f5..9c1a0a8a0b60 100644 --- a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeAction.java +++ b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeAction.java @@ -16,9 +16,9 @@ package com.intellij.openapi.diff.impl.dir.actions; import com.intellij.ide.diff.DirDiffSettings; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.diff.impl.dir.DirDiffTableModel; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.util.PlatformIcons; import com.intellij.util.ui.EmptyIcon; @@ -27,7 +27,7 @@ import javax.swing.*; /** * @author Konstantin Bulenkov */ -class ChangeCompareModeAction extends AnAction { +class ChangeCompareModeAction extends DumbAwareAction { private final static Icon ON = PlatformIcons.CHECK_ICON; private final static Icon ON_SELECTED = PlatformIcons.CHECK_ICON_SELECTED; private final static Icon OFF = EmptyIcon.create(ON.getIconHeight()); diff --git a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeGroup.java b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeGroup.java index 89a6c7e823c0..fe97eec6079f 100644 --- a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeGroup.java +++ b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/ChangeCompareModeGroup.java @@ -19,6 +19,7 @@ import com.intellij.ide.diff.DirDiffSettings; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ComboBoxAction; import com.intellij.openapi.diff.impl.dir.DirDiffTableModel; +import com.intellij.openapi.project.DumbAware; import com.intellij.ui.IdeBorderFactory; import org.jetbrains.annotations.NotNull; @@ -29,7 +30,7 @@ import java.util.ArrayList; /** * @author Konstantin Bulenkov */ -public class ChangeCompareModeGroup extends ComboBoxAction implements ShortcutProvider{ +public class ChangeCompareModeGroup extends ComboBoxAction implements ShortcutProvider, DumbAware { private final DefaultActionGroup myGroup; private DirDiffSettings mySettings; private JButton myButton; diff --git a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java index f7a79838fd0a..abf938e5a623 100644 --- a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java +++ b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.ShortcutProvider; import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.diff.impl.dir.DirDiffTableModel; +import com.intellij.openapi.project.DumbAware; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -27,7 +28,7 @@ import javax.swing.*; /** * @author Konstantin Bulenkov */ -public abstract class DirDiffAction extends ToggleAction implements ShortcutProvider { +public abstract class DirDiffAction extends ToggleAction implements ShortcutProvider, DumbAware { private final DirDiffTableModel myModel; protected DirDiffAction(DirDiffTableModel model) { diff --git a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetNoOperation.java b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetNoOperation.java index c1c57e1b441c..c31a07ce5cbd 100644 --- a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetNoOperation.java +++ b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetNoOperation.java @@ -16,10 +16,10 @@ package com.intellij.openapi.diff.impl.dir.actions.popup; import com.intellij.ide.diff.DirDiffOperation; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.diff.impl.dir.DirDiffElementImpl; import com.intellij.openapi.diff.impl.dir.DirDiffTableModel; +import com.intellij.openapi.project.DumbAwareAction; import javax.swing.*; @@ -27,7 +27,7 @@ import javax.swing.*; * @author lene * Date: 23.04.12 */ -public class SetNoOperation extends AnAction { +public class SetNoOperation extends DumbAwareAction { @Override public void actionPerformed(AnActionEvent e) { final DirDiffTableModel model = SetOperationToBase.getModel(e); diff --git a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetOperationToBase.java b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetOperationToBase.java index 81e1968caded..747f5252b736 100644 --- a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetOperationToBase.java +++ b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/actions/popup/SetOperationToBase.java @@ -17,11 +17,11 @@ package com.intellij.openapi.diff.impl.dir.actions.popup; import com.intellij.ide.diff.DirDiffElement; import com.intellij.ide.diff.DirDiffOperation; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.diff.impl.dir.DirDiffElementImpl; import com.intellij.openapi.diff.impl.dir.DirDiffPanel; import com.intellij.openapi.diff.impl.dir.DirDiffTableModel; +import com.intellij.openapi.project.DumbAwareAction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,7 +30,7 @@ import javax.swing.*; /** * @author Konstantin Bulenkov */ -public abstract class SetOperationToBase extends AnAction { +public abstract class SetOperationToBase extends DumbAwareAction { @Override public void actionPerformed(AnActionEvent e) { DirDiffOperation operation = getOperation(); diff --git a/platform/diff-impl/tests/com/intellij/diff/DiffColorSettingsTest.kt b/platform/diff-impl/tests/com/intellij/diff/DiffColorSettingsTest.kt new file mode 100644 index 000000000000..1c2be0a7e4e4 --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/DiffColorSettingsTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff + +import com.intellij.diff.tools.simple.SimpleThreesideDiffViewer +import com.intellij.diff.util.TextDiffType +import com.intellij.openapi.diff.impl.settings.DiffPreviewPanel +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.openapi.editor.markup.RangeHighlighter +import com.intellij.openapi.util.Disposer +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory + +class DiffColorSettingsTest : DiffTestCase() { + private var projectFixture: IdeaProjectTestFixture? = null + + override fun setUp() { + super.setUp() + projectFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getTestName(true)).getFixture() + projectFixture!!.setUp() + } + + override fun tearDown() { + projectFixture?.tearDown() + super.tearDown() + } + + fun testChanges() { + val disposable = Disposer.newDisposable() + try { + val panel = DiffPreviewPanel(disposable) + val viewer = panel.testGetViewer() + + assertEquals(viewer.changes.size, 6) + assertContainsRange(viewer, TextDiffType.MODIFIED) + assertContainsRange(viewer, TextDiffType.INSERTED) + assertContainsRange(viewer, TextDiffType.DELETED) + assertContainsRange(viewer, TextDiffType.CONFLICT) + + assertContainsBackgroundColor(viewer, TextDiffType.MODIFIED, true) + assertContainsBackgroundColor(viewer, TextDiffType.INSERTED, true) + assertContainsBackgroundColor(viewer, TextDiffType.DELETED, true) + assertContainsBackgroundColor(viewer, TextDiffType.CONFLICT, true) + + assertContainsBackgroundColor(viewer, TextDiffType.MODIFIED, false) + assertContainsBackgroundColor(viewer, TextDiffType.INSERTED, false) + assertContainsBackgroundColor(viewer, TextDiffType.DELETED, false) + assertContainsBackgroundColor(viewer, TextDiffType.CONFLICT, false) + + assertContainsMarkerColor(viewer, TextDiffType.MODIFIED) + assertContainsMarkerColor(viewer, TextDiffType.INSERTED) + assertContainsMarkerColor(viewer, TextDiffType.DELETED) + assertContainsMarkerColor(viewer, TextDiffType.CONFLICT) + } + finally { + Disposer.dispose(disposable) + } + } + + private fun assertContainsRange(viewer: SimpleThreesideDiffViewer, type: TextDiffType) { + assertNotNull(viewer.changes.find { change -> + change.diffType == type + }) + } + + private fun assertContainsBackgroundColor(viewer: SimpleThreesideDiffViewer, type: TextDiffType, isIgnored: Boolean) { + assertContainsBackgroundColor(viewer) { editor, highlighter -> + val actual = highlighter.textAttributes?.backgroundColor + val expected = if (isIgnored) type.getIgnoredColor(editor) else type.getColor(editor) + actual == expected + } + } + + private fun assertContainsMarkerColor(viewer: SimpleThreesideDiffViewer, type: TextDiffType) { + assertContainsBackgroundColor(viewer) { editor, highlighter -> + highlighter.textAttributes?.errorStripeColor == type.getMarkerColor(editor) + } + } + + private fun assertContainsBackgroundColor(viewer: SimpleThreesideDiffViewer, condition: (EditorEx, RangeHighlighter) -> Boolean) { + val ranges = viewer.editors.flatMap { editor -> + listOfNotNull(editor.markupModel.allHighlighters.map { highlighter -> + return@map if (condition(editor, highlighter)) highlighter else null + }) + } + assertTrue(ranges.isNotEmpty()) + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java index cff59c10fddf..122967f09e88 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java @@ -51,8 +51,9 @@ public class CompletionLookupArranger extends LookupArranger { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CompletionLookupArranger"); @Nullable private static StatisticsUpdate ourPendingUpdate; private static final Alarm ourStatsAlarm = new Alarm(ApplicationManager.getApplication()); - private static final Key PRESENTATION_INVARIANT = Key.create("PRESENTATION_INVARIANT"); - private static final Comparator BY_PRESENTATION_COMPARATOR = new Comparator() { + private static final Key GLOBAL_PRESENTATION_INVARIANT = Key.create("PRESENTATION_INVARIANT"); + private final Key PRESENTATION_INVARIANT = Key.create("PRESENTATION_INVARIANT"); + private final Comparator BY_PRESENTATION_COMPARATOR = new Comparator() { @Override public int compare(LookupElement o1, LookupElement o2) { String invariant = PRESENTATION_INVARIANT.get(o1); @@ -152,6 +153,7 @@ public class CompletionLookupArranger extends LookupArranger { final String invariant = presentation.getItemText() + "\0###" + getTailTextOrSpace(presentation) + "###" + presentation.getTypeText(); element.putUserData(PRESENTATION_INVARIANT, invariant); + element.putUserData(GLOBAL_PRESENTATION_INVARIANT, invariant); CompletionSorterImpl sorter = obtainSorter(element); Classifier classifier = myClassifiers.get(sorter); @@ -208,7 +210,7 @@ public class CompletionLookupArranger extends LookupArranger { return tailText == null || tailText.isEmpty() ? " " : tailText; } - private static List sortByPresentation(Iterable source, LookupImpl lookup) { + private List sortByPresentation(Iterable source, LookupImpl lookup) { ArrayList startMatches = ContainerUtil.newArrayList(); ArrayList middleMatches = ContainerUtil.newArrayList(); for (LookupElement element : source) { @@ -369,7 +371,7 @@ public class CompletionLookupArranger extends LookupArranger { return new CompletionLookupArranger(myParameters, myProcess); } - private static int getItemToSelect(LookupImpl lookup, List items, boolean onExplicitAction, @Nullable LookupElement mostRelevant) { + private int getItemToSelect(LookupImpl lookup, List items, boolean onExplicitAction, @Nullable LookupElement mostRelevant) { if (items.isEmpty() || lookup.getFocusDegree() == LookupImpl.FocusDegree.UNFOCUSED) { return 0; } @@ -391,7 +393,7 @@ public class CompletionLookupArranger extends LookupArranger { for (int i = 0; i < items.size(); i++) { String invariant = PRESENTATION_INVARIANT.get(items.get(i)); - if (invariant != null && invariant.equals(PRESENTATION_INVARIANT.get(lastSelection))) { + if (invariant != null && invariant.equals(GLOBAL_PRESENTATION_INVARIANT.get(lastSelection))) { return i; } } @@ -577,7 +579,7 @@ public class CompletionLookupArranger extends LookupArranger { } } - private static class AlphaClassifier extends Classifier { + private class AlphaClassifier extends Classifier { private final LookupImpl myLookup; private AlphaClassifier(LookupImpl lookup) { diff --git a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java index 2e84ade5dae7..e7ee5311220e 100644 --- a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java @@ -45,6 +45,7 @@ import com.intellij.util.EventDispatcher; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.awt.*; import java.util.List; @@ -229,4 +230,10 @@ public class DiffPreviewPanel implements PreviewPanel { @Override public void disposeUIResources() { } + + @NotNull + @TestOnly + public SimpleThreesideDiffViewer testGetViewer() { + return myViewer; + } } diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java index c698c4bfb138..85d6150dbbbd 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java @@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; @@ -38,6 +39,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class InstalledPackagesPanel extends JPanel { + private static final Logger LOG = Logger.getInstance(InstalledPackagesPanel.class); private final AnActionButton myUpgradeButton; protected final AnActionButton myInstallButton; private final AnActionButton myUninstallButton; @@ -418,7 +420,7 @@ public class InstalledPackagesPanel extends JPanel { packages = packageManagementService.getInstalledPackages(); } catch (IOException e) { - // do nothing, we already have an empty list + LOG.warn(e.getMessage()); // do nothing, we already have an empty list } finally { final Collection finalPackages = packages; diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java index 2d6b32ae5a01..a398efccbdec 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java @@ -25,6 +25,8 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.ex.util.EditorUIUtil; +import com.intellij.openapi.editor.ex.util.EditorUtil; +import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.HintHint; @@ -57,8 +59,12 @@ public class EditorFragmentComponent extends JPanel { private void doInit(EditorEx editor, int startLine, int endLine, boolean showFolding, boolean showGutter) { Document doc = editor.getDocument(); final int endOffset = endLine < doc.getLineCount() ? doc.getLineEndOffset(endLine) : doc.getTextLength(); - final int textImageWidth = Math.min(editor.getMaxWidthInRange(doc.getLineStartOffset(startLine), endOffset), ScreenUtil - .getScreenRectangle(1, 1).width); + boolean newRendering = editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering; + int widthAdjustment = newRendering ? EditorUtil.getSpaceWidth(Font.PLAIN, editor) : 0; + final int textImageWidth = Math.min( + editor.getMaxWidthInRange(doc.getLineStartOffset(startLine), endOffset) + widthAdjustment, + ScreenUtil.getScreenRectangle(1, 1).width + ); LOG.assertTrue(textImageWidth > 0, "TextWidth: "+textImageWidth+"; startLine:" + startLine + "; endLine:" + endLine + ";"); FoldingModelEx foldingModel = editor.getFoldingModel(); @@ -74,7 +80,7 @@ public class EditorFragmentComponent extends JPanel { final int textImageHeight = y2 - y1 == 0 ? editor.getLineHeight() : y2 - y1; LOG.assertTrue(textImageHeight > 0, "Height: " + textImageHeight + "; startLine:" + startLine + "; endLine:" + endLine + "; p1:" + p1 + "; p2:" + p2); - int savedScrollOffset = editor.getScrollingModel().getHorizontalScrollOffset(); + int savedScrollOffset = newRendering ? 0 : editor.getScrollingModel().getHorizontalScrollOffset(); if (savedScrollOffset > 0) { editor.getScrollingModel().scrollHorizontally(0); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 19b009d06e63..836a5a4f2fe8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -3152,7 +3152,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi + ", soft wraps data: " + getSoftWrapModel().dumpState() + "\n\nfolding data: " + getFoldingModel().dumpState() + (myDocument instanceof DocumentImpl ? "\n\ndocument info: " + ((DocumentImpl)myDocument).dumpState() : "") - + "\nfont preferences: " + myScheme.getFontPreferences(); + + "\nfont preferences: " + myScheme.getFontPreferences() + + "\npure painting mode: " + myPurePaintingMode + + (myView == null ? "" : "\nview: " + myView.dumpState()); } private class CachedFontContent { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java index e757a717042e..25f0b0c0989c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java @@ -15,7 +15,10 @@ */ package com.intellij.openapi.editor.impl.view; +import com.intellij.diagnostic.Dumpable; import com.intellij.openapi.Disposable; +import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.EditorLinePainter; import com.intellij.openapi.editor.FoldRegion; @@ -45,7 +48,9 @@ import java.util.List; /** * Calculates width (in pixels) of editor contents. */ -class EditorSizeManager implements PrioritizedDocumentListener, Disposable, FoldingListener { +class EditorSizeManager implements PrioritizedDocumentListener, Disposable, FoldingListener, Dumpable { + private static final Logger LOG = Logger.getInstance(EditorSizeManager.class); + private static final int UNKNOWN_WIDTH = Integer.MAX_VALUE; private final EditorView myView; @@ -197,6 +202,10 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold } private int calculatePreferredWidth() { + if (myLineWidths.size() != myEditor.getVisibleLineCount()) { + LOG.error("Inconsistent state", new Attachment("editor.txt", myEditor.dumpState())); + reset(); + } assert myLineWidths.size() == myEditor.getVisibleLineCount(); VisualLinesIterator iterator = new VisualLinesIterator(myView, 0); int maxWidth = 0; @@ -324,4 +333,10 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold myEditor.getFoldingModel().setFoldingEnabled(foldingEnabled); } } + + @NotNull + @Override + public String dumpState() { + return "[line widths: " + myLineWidths + "]"; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java index b5556ec6ece6..01e81ece567b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.editor.impl.view; +import com.intellij.diagnostic.Dumpable; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.FoldRegion; @@ -44,7 +45,7 @@ import java.awt.image.BufferedImage; * * Also contains a cache of several font-related quantities (line height, space width, etc). */ -public class EditorView implements TextDrawingCallback, Disposable { +public class EditorView implements TextDrawingCallback, Disposable, Dumpable { private static Key FOLD_REGION_TEXT_LAYOUT = Key.create("text.layout"); private final EditorImpl myEditor; @@ -244,6 +245,7 @@ public class EditorView implements TextDrawingCallback, Disposable { public Dimension getPreferredSize() { assertIsDispatchThread(); + assert !myEditor.isPurePaintingMode(); myEditor.getSoftWrapModel().prepareToMapping(); return mySizeManager.getPreferredSize(); } @@ -467,4 +469,10 @@ public class EditorView implements TextDrawingCallback, Disposable { public void drawChars(@NotNull Graphics g, @NotNull char[] data, int start, int end, int x, int y, Color color, FontInfo fontInfo) { myPainter.drawChars(g, data, start, end, x, y, color, fontInfo); } + + @NotNull + @Override + public String dumpState() { + return "[Size manager: " + mySizeManager.dumpState() + "]"; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java index ed9185a43230..202999b53b90 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java @@ -173,7 +173,7 @@ public final class IdeKeyEventDispatcher implements Disposable { return false; } if (focusOwner instanceof JTextComponent && ((JTextComponent)focusOwner).isEditable()) { - if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED && e.getKeyChar() != KeyEvent.VK_ESCAPE) { + if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED && e.getKeyCode() != KeyEvent.VK_ESCAPE) { MacUIUtil.hideCursor(); } } diff --git a/platform/platform-resources/src/META-INF/xdebugger.xml b/platform/platform-resources/src/META-INF/xdebugger.xml index fef5c275b5cd..511f4f24856b 100644 --- a/platform/platform-resources/src/META-INF/xdebugger.xml +++ b/platform/platform-resources/src/META-INF/xdebugger.xml @@ -30,7 +30,7 @@ - diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImpl.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImpl.java index 56f5617480b2..296942efd5d5 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImpl.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImpl.java @@ -10,6 +10,7 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ContentIterator; import com.intellij.openapi.util.Computable; @@ -47,7 +48,7 @@ public class MatcherImpl { private static final Logger LOG = Logger.getInstance("#com.intellij.structuralsearch.impl.matcher.MatcherImpl"); // project being worked on private final Project project; - private final PsiDocumentManager documentManager; + private final DumbService myDumbService; // context of matching private final MatchContext matchContext; @@ -70,7 +71,7 @@ public class MatcherImpl { matchContext.setOptions(matchOptions); cacheCompiledPattern(matchOptions, PatternCompiler.compilePattern(project,matchOptions)); } - documentManager = PsiDocumentManager.getInstance(this.project); + myDumbService = DumbService.getInstance(project); } static class LastMatchData { @@ -523,7 +524,7 @@ public class MatcherImpl { matchContext.getSink().processFile((PsiFile)file); } - documentManager.commitAndRunReadAction(new Runnable() { + myDumbService.runReadActionInSmartMode(new Runnable() { @Override public void run() { if (!file.isValid()) return; diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/AnnotationGutterActionProvider.java b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/AnnotationGutterActionProvider.java index d3bc6fcda482..a9fa4e022e4f 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/AnnotationGutterActionProvider.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/AnnotationGutterActionProvider.java @@ -20,9 +20,7 @@ import com.intellij.openapi.extensions.ExtensionPointName; import org.jetbrains.annotations.NotNull; /** - * Implement this to add additional custom actions to the popup invoked by right-clicking on the annotation gutter. - * - * @author Kirill Likhodedov + * Implement this to add an additional custom action to the annotation gutter popup (invoked by right-click to an annotation). */ public interface AnnotationGutterActionProvider { @@ -30,10 +28,11 @@ public interface AnnotationGutterActionProvider { /** * Create an action that will be added to the annotation gutter popup. + * * @param annotation annotation which is currently shown on the gutter. * @return new action that can be invoked from the annotation gutter popup. */ @NotNull - AnAction createAction(FileAnnotation annotation); + AnAction createAction(@NotNull FileAnnotation annotation); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/merge/MergeDialogCustomizer.java b/platform/vcs-api/src/com/intellij/openapi/vcs/merge/MergeDialogCustomizer.java index 4a8574d48ea7..1d8fd2d654ad 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/merge/MergeDialogCustomizer.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/merge/MergeDialogCustomizer.java @@ -26,7 +26,6 @@ import java.util.Collection; /** * Provides custom titles and messages used in MultipleFileMergeDialog and DiffTool invoked from it. - * @author Kirill Likhodedov */ public class MergeDialogCustomizer { @@ -34,7 +33,7 @@ public class MergeDialogCustomizer { * @param files files that have conflicted changes and are shown in the dialog. * @return description that is shows above the list of conflicted files. Null (which is equivalent to empty) by default. */ - public @Nullable String getMultipleFileMergeDescription(Collection files) { + @Nullable public String getMultipleFileMergeDescription(@NotNull Collection files) { return null; } @@ -42,7 +41,7 @@ public class MergeDialogCustomizer { * @param file file that is merged. * @return title of the merge dialog invoked for a 3-way merge of a file. */ - public @Nullable String getMergeWindowTitle(VirtualFile file) { + @Nullable public String getMergeWindowTitle(@NotNull VirtualFile file) { return VcsBundle.message("multiple.file.merge.request.title", FileUtil.toSystemDependentName(file.getPresentableUrl())); } @@ -50,7 +49,7 @@ public class MergeDialogCustomizer { * @param file file that is merged. * @return title that is shown above the left panel in the 3-way merge dialog. "Local changes" by default. */ - public @Nullable String getLeftPanelTitle(VirtualFile file) { + @Nullable public String getLeftPanelTitle(@NotNull VirtualFile file) { return VcsBundle.message("merge.version.title.local.changes"); } @@ -58,18 +57,18 @@ public class MergeDialogCustomizer { * @param file file that is merged. * @return title that is shown above the center panel in the 3-way merge dialog. "Merge result" by default. */ - public @Nullable String getCenterPanelTitle(VirtualFile file) { + @Nullable public String getCenterPanelTitle(@NotNull VirtualFile file) { return VcsBundle.message("merge.version.title.merge.result"); } /** - * @param file file that is merged. - * @param lastRevisionNumber + * @param file file that is being merged. + * @param revisionNumber revision number of the file at the right, can be null if unknown. * @return title that is shown above the right panel in the 3-way merge dialog. "Changes from server" with the revision number by default. */ - public @Nullable String getRightPanelTitle(VirtualFile file, VcsRevisionNumber lastRevisionNumber) { - if (lastRevisionNumber != null) { - return VcsBundle.message("merge.version.title.last.version.number", lastRevisionNumber.asString()); + @Nullable public String getRightPanelTitle(@NotNull VirtualFile file, @Nullable VcsRevisionNumber revisionNumber) { + if (revisionNumber != null) { + return VcsBundle.message("merge.version.title.last.version.number", revisionNumber.asString()); } else { return VcsBundle.message("merge.version.title.last.version"); } @@ -77,11 +76,9 @@ public class MergeDialogCustomizer { /** * @return The title of multiple files merge dialog. - * Don't mix with {@link #getMergeWindowTitle(com.intellij.openapi.vfs.VirtualFile)} which is the title of a 3-way merge dialog - * displayed for a single file. + * Don't mix with {@link #getMergeWindowTitle(VirtualFile)} which is the title of a 3-way merge dialog displayed for a single file. */ - public @NotNull String getMultipleFileDialogTitle() { + @NotNull public String getMultipleFileDialogTitle() { return VcsBundle.message("multiple.file.merge.title"); } - } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java index 7aed6e23badd..05beec35ecf7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.CloneUtils; import com.siyeh.ig.psiutils.MethodUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -33,7 +34,7 @@ public class CastToConcreteClassInspection extends BaseInspection { public boolean ignoreAbstractClasses = false; @SuppressWarnings("PublicField") - public boolean ignoreInEquals = true; + public boolean ignoreInEquals = true; // keep for compatibility @Override @NotNull @@ -73,11 +74,9 @@ public class CastToConcreteClassInspection extends BaseInspection { if (!ConcreteClassUtil.typeIsConcreteClass(typeElement, ignoreAbstractClasses)) { return; } - if (ignoreInEquals) { - final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiClass.class, PsiLambdaExpression.class); - if (MethodUtils.isEquals(method)) { - return; - } + final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiClass.class, PsiLambdaExpression.class); + if (MethodUtils.isEquals(method) || CloneUtils.isClone(method)) { + return; } registerError(typeElement, typeElement.getType()); } @@ -116,11 +115,9 @@ public class CastToConcreteClassInspection extends BaseInspection { if (!ConcreteClassUtil.typeIsConcreteClass(parameter, ignoreAbstractClasses)) { return; } - if (ignoreInEquals) { - final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiClass.class, PsiLambdaExpression.class); - if (MethodUtils.isEquals(method)) { - return; - } + final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiClass.class, PsiLambdaExpression.class); + if (MethodUtils.isEquals(method) || CloneUtils.isClone(method)) { + return; } registerMethodCallError(expression, parameter); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java index 917a29cf687d..f2c5bb3e63c3 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java @@ -15,8 +15,11 @@ */ package com.siyeh.ig.errorhandling; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; +import com.intellij.psi.controlFlow.DefUseUtil; import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -60,13 +63,7 @@ public class UnnecessaryInitCauseInspectionBase extends BaseInspection { return; } final PsiExpressionList argumentList = expression.getArgumentList(); - if (!ExpressionUtils.hasExpressionCount(argumentList, 1)) { - return; - } - final PsiExpression argument = ExpressionUtils.getFirstExpressionInList(argumentList); - if (argument == null) { - return; - } + final PsiExpression argument = ExpressionUtils.getOnlyExpressionInList(argumentList); if (!TypeUtils.expressionHasTypeOrSubtype(argument, CommonClassNames.JAVA_LANG_THROWABLE)) { return; } @@ -80,12 +77,42 @@ public class UnnecessaryInitCauseInspectionBase extends BaseInspection { } final PsiExpression qualifier = ParenthesesUtils.stripParentheses(methodExpression.getQualifierExpression()); final PsiNewExpression newExpression = findNewExpression(qualifier); - if (!isCauseConstructorAvailable(newExpression)) { + if (!isCauseConstructorAvailable(newExpression) || !canExpressionBeMovedBackwards(argument, newExpression)) { return; } registerMethodCallError(expression); } + private static boolean canExpressionBeMovedBackwards(final PsiExpression cause, final PsiExpression newLocation) { + if (cause == null || newLocation == null) return false; + assert cause.getTextOffset() > newLocation.getTextOffset(); + final PsiCodeBlock block = PsiTreeUtil.getParentOfType(cause, PsiCodeBlock.class); + final PsiCodeBlock newBlock = PsiTreeUtil.getParentOfType(newLocation, PsiCodeBlock.class); + if (block == null || newBlock == null || !PsiTreeUtil.isAncestor(block, newBlock, false)) return false; + final int offset = newLocation.getTextOffset(); + final Ref result = new Ref(Boolean.TRUE); + cause.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + if (!result.get().booleanValue()) { + return; + } + super.visitReferenceExpression(expression); + final PsiElement target = expression.resolve(); + if (!(target instanceof PsiVariable)) { + return; + } + final PsiElement[] defs = DefUseUtil.getDefs(block, (PsiVariable)target, cause); + for (PsiElement def : defs) { + if (def.getTextOffset() > offset) { + result.set(Boolean.FALSE); + } + } + } + }); + return result.get().booleanValue(); + } + public static boolean isCauseConstructorAvailable(PsiNewExpression newExpression) { if (newExpression == null) { return false; @@ -140,10 +167,25 @@ public class UnnecessaryInitCauseInspectionBase extends BaseInspection { } final PsiVariable variable = (PsiVariable)target; final PsiExpression initializer = variable.getInitializer(); - if (!(initializer instanceof PsiNewExpression)) { + if ((initializer instanceof PsiNewExpression)) { + return (PsiNewExpression)initializer; + } + final PsiCodeBlock block = PsiTreeUtil.getParentOfType(target, PsiCodeBlock.class); + final PsiElement[] defs = DefUseUtil.getDefs(block, variable, expression); + if (defs.length != 1) { return null; } - return (PsiNewExpression)initializer; + final PsiElement def = defs[0]; + if (!(def instanceof PsiReferenceExpression) || !(def.getParent() instanceof PsiAssignmentExpression)) { + return null; + } + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)def.getParent(); + if (assignmentExpression.getOperationTokenType() != JavaTokenType.EQ) return null; + final PsiExpression rhs = ParenthesesUtils.stripParentheses(assignmentExpression.getRExpression()); + if (!(rhs instanceof PsiNewExpression)) { + return null; + } + return (PsiNewExpression)rhs; } return null; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java index 7010e63084bc..a09e2143f4ab 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ControlFlowUtils.java @@ -478,7 +478,7 @@ public class ControlFlowUtils { return getOnlyChildOfType(codeBlock, PsiStatement.class); } - private static T getOnlyChildOfType(@Nullable PsiElement element, @NotNull Class aClass) { + static T getOnlyChildOfType(@Nullable PsiElement element, @NotNull Class aClass) { if (element == null) return null; T result = null; for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java index 294ff8bbb583..799f3dac1aff 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java @@ -72,6 +72,11 @@ public class ExpressionUtils { return PsiTreeUtil.getChildOfType(expressionList, PsiExpression.class); } + @Nullable + public static PsiExpression getOnlyExpressionInList(@Nullable PsiExpressionList expressionList) { + return ControlFlowUtils.getOnlyChildOfType(expressionList, PsiExpression.class); + } + public static boolean isDeclaredConstant(PsiExpression expression) { PsiField field = PsiTreeUtil.getParentOfType(expression, PsiField.class); diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html b/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html index 1ae40923ea45..503c2af6c0d2 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html @@ -2,12 +2,10 @@ Reports casting a value to a concrete class, rather than an interface. Such declarations may represent a failure of abstraction, and may make testing more difficult. -Declarations whose classes come from system or third-party libraries will not be reported by this inspection. +Casts to system or library classes and casts inside equals() and clone() methods will not be reported by this inspection.

-Use the first checkbox below to have this inspection ignore casts to abstract classes. -

-Use the second checkbox below to have this inspection ignore casts inside equals() methods. +Use the checkbox below to have this inspection ignore casts to abstract classes.

diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.after.java index 8332ee58e6e7..1b89ce8810e5 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.after.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.after.java @@ -7,7 +7,7 @@ class Indirect { new FileInputStream("asdf"); } catch (FileNotFoundException e) { final RuntimeException exception = new RuntimeException(e); - throw e; + throw exception; } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.java index fe3b01bd1126..2a0ca17c13d0 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/errorhandling/unnecessary_init_cause/Indirect.java @@ -8,7 +8,7 @@ class Indirect { } catch (FileNotFoundException e) { final RuntimeException exception = new RuntimeException(); exception.initCause(e); - throw e; + throw exception; } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/CastToConcreteClass.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/CastToConcreteClass.java index a4868a962999..aa6ce961a68e 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/CastToConcreteClass.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/CastToConcreteClass.java @@ -14,10 +14,15 @@ class CastToConcreteClass { } } + @Override + public CastToConcreteClass clone() throws CloneNotSupportedException { + return (CastToConcreteClass) super.clone(); + } + void foo(Object o) { - CastToConcreteClass c = (CastToConcreteClass)o; - CastToConcreteClass c2 = CastToConcreteClass.class.cast(o); + CastToConcreteClass c = (CastToConcreteClass)o; + CastToConcreteClass c2 = CastToConcreteClass.class.cast(o); final Class aClass = CastToConcreteClass.class; - final CastToConcreteClass c3 = aClass.cast(o); + final CastToConcreteClass c3 = aClass.cast(o); } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/expected.xml deleted file mode 100644 index 76af465c4b12..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/expected.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - CastToConcreteClass.java - 18 - Cast to a concrete class - Cast to concrete class <code>CastToConcreteClass</code> #loc - - - - CastToConcreteClass.java - 19 - Cast to a concrete class - Cast to concrete class <code>CastToConcreteClass</code> #loc - - - - CastToConcreteClass.java - 21 - Cast to a concrete class - Cast to concrete class <code>CastToConcreteClass</code> #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/object_to_string/ObjectToString.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/object_to_string/ObjectToString.java index 4fda0ef30cb3..2953f4c898d2 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/object_to_string/ObjectToString.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/object_to_string/ObjectToString.java @@ -17,10 +17,10 @@ class ObjectToString class N {} void foo(N n) { - n.toString(); + n.toString(); } String bar(N n) { - return "n: " + n + "a"; + return "n: " + n + "a"; } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/object_to_string/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/object_to_string/expected.xml deleted file mode 100644 index 2628b6f1f6b7..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/object_to_string/expected.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - ObjectToString.java - 20 - Call to default 'toString()' - Call to default 'toString()' on <code>n</code> #loc - - - - ObjectToString.java - 24 - Call to default 'toString()' - Call to default 'toString()' on <code>n</code> #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/abstraction/CastToConcreteClassInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/abstraction/CastToConcreteClassInspectionTest.java index bd9e6e35ba9a..b5ae8b31a4a8 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/abstraction/CastToConcreteClassInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/abstraction/CastToConcreteClassInspectionTest.java @@ -1,12 +1,20 @@ package com.siyeh.ig.abstraction; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class CastToConcreteClassInspectionTest extends IGInspectionTestCase { +public class CastToConcreteClassInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - final CastToConcreteClassInspection tool = new CastToConcreteClassInspection(); - tool.ignoreInEquals = true; - doTest("com/siyeh/igtest/abstraction/cast_to_concrete_class", tool); + public void testCastToConcreteClass() { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + final CastToConcreteClassInspection inspection = new CastToConcreteClassInspection(); + inspection.ignoreInEquals = true; + return inspection; } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ObjectToStringInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ObjectToStringInspectionTest.java index d81471cf88a0..36c47f491c7c 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ObjectToStringInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ObjectToStringInspectionTest.java @@ -1,11 +1,18 @@ package com.siyeh.ig.bugs; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class ObjectToStringInspectionTest extends IGInspectionTestCase { +public class ObjectToStringInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - doTest("com/siyeh/igtest/bugs/object_to_string", - new ObjectToStringInspection()); + public void testObjectToString() throws Exception { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new ObjectToStringInspection(); } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionTest.java new file mode 100644 index 000000000000..2e3a36d80ac4 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.errorhandling; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class UnnecessaryInitCauseInspectionTest extends LightInspectionTestCase { + + public void testSplitDeclarationAssignment() { + doMemberTest("void foo() {\n" + + " RuntimeException exception = null;\n" + + " try {\n" + + " new java.io.FileInputStream(\"asdf\");\n" + + " } catch (java.io.FileNotFoundException e) {\n" + + " exception = new RuntimeException();\n" + + " exception./*Unnecessary 'Throwable.initCause()' call*/initCause/**/(e);\n" + + " } catch (RuntimeException e) {\n" + + " exception = e;\n" + + " }\n" + + " throw exception;\n" + + "}"); + } + + public void testReassigned() { + doMemberTest("void foo() {\n" + + " try {\n" + + " new java.io.FileInputStream(\"asdf\");\n" + + " } catch (java.io.FileNotFoundException e) {\n" + + " RuntimeException exception = new RuntimeException();\n" + + " e = null;\n" + + " exception.initCause(e);\n" + + " throw exception;\n" + + " }\n" + + "}"); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new UnnecessaryInitCauseInspection(); + } +} \ No newline at end of file diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index 80b942563d93..d35bd926eea0 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -137,6 +137,7 @@ + files) { + public String getMultipleFileMergeDescription(@NotNull Collection files) { return "Conflicts during cherry-picking commit " + myCommitHash + " made by " + myCommitAuthor + "
" + "\"" + myCommitMessage + "\""; } @Override - public String getLeftPanelTitle(VirtualFile file) { + public String getLeftPanelTitle(@NotNull VirtualFile file) { return "Local changes"; } @Override - public String getRightPanelTitle(VirtualFile file, VcsRevisionNumber lastRevisionNumber) { + public String getRightPanelTitle(@NotNull VirtualFile file, VcsRevisionNumber revisionNumber) { return "Changes from cherry-pick " + myCommitHash + ""; } } diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java index 27d22010dcd8..982ad2572b43 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java @@ -38,7 +38,7 @@ import git4idea.GitVcs; import git4idea.changes.GitChangeUtils; import git4idea.config.GitExecutableValidator; import git4idea.history.browser.SHAHash; -import git4idea.history.wholeTree.SelectRevisionInGitLogAction; +import git4idea.log.GitShowCommitInLogAction; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; @@ -69,7 +69,7 @@ public class GitHistoryProvider implements VcsHistoryProviderEx, VcsCacheableHis return new AnAction[] { ShowAllAffectedGenericAction.getInstance(), ActionManager.getInstance().getAction(VcsActions.ACTION_COPY_REVISION_NUMBER), - new SelectRevisionInGitLogAction() }; + new GitShowCommitInLogAction() }; } public boolean isDateOmittable() { diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java b/plugins/git4idea/src/git4idea/log/GitShowCommitInLogAction.java similarity index 83% rename from plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java rename to plugins/git4idea/src/git4idea/log/GitShowCommitInLogAction.java index 0d4c66a44f8e..e2aa69d0ff80 100644 --- a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java +++ b/plugins/git4idea/src/git4idea/log/GitShowCommitInLogAction.java @@ -1,4 +1,19 @@ -package git4idea.history.wholeTree; +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea.log; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -31,11 +46,11 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -public class SelectRevisionInGitLogAction extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(SelectRevisionInGitLogAction.class); +public class GitShowCommitInLogAction extends DumbAwareAction { + private static final Logger LOG = Logger.getInstance(GitShowCommitInLogAction.class); - public SelectRevisionInGitLogAction() { - super(GitBundle.getString("vcs.history.action.gitlog"), GitBundle.getString("vcs.history.action.gitlog"), null); + public GitShowCommitInLogAction() { + super(GitBundle.getString("vcs.history.action.gitlog")); } @Override @@ -106,7 +121,7 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { } @Nullable - private static VcsRevisionNumber getRevisionNumber(@NotNull AnActionEvent event) { + protected VcsRevisionNumber getRevisionNumber(@NotNull AnActionEvent event) { VcsRevisionNumber revision = event.getData(VcsDataKeys.VCS_REVISION_NUMBER); if (revision == null) { VcsFileRevision fileRevision = event.getData(VcsDataKeys.VCS_FILE_REVISION); @@ -122,8 +137,7 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { super.update(e); e.getPresentation().setEnabled(e.getProject() != null && VcsLogContentProvider.findLogManager(e.getProject()) != null && - (e.getData(VcsDataKeys.VCS_FILE_REVISION) != null || - e.getData(VcsDataKeys.VCS_REVISION_NUMBER) != null)); + getRevisionNumber(e) != null); } @Nullable diff --git a/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java b/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java index f04946b75170..1ccbc7ad3258 100644 --- a/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java +++ b/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java @@ -74,7 +74,7 @@ public class GitConflictResolver { private String myErrorNotificationAdditionalDescription = ""; private String myMergeDescription = ""; private MergeDialogCustomizer myMergeDialogCustomizer = new MergeDialogCustomizer() { - @Override public String getMultipleFileMergeDescription(Collection files) { + @Override public String getMultipleFileMergeDescription(@NotNull Collection files) { return myMergeDescription; } }; diff --git a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java index 38db08222aea..06a32772cefb 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java @@ -216,17 +216,17 @@ public class GitStashChangesSaver extends GitChangesSaver { private static class UnstashMergeDialogCustomizer extends MergeDialogCustomizer { @Override - public String getMultipleFileMergeDescription(Collection files) { + public String getMultipleFileMergeDescription(@NotNull Collection files) { return "Uncommitted changes that were stashed before update have conflicts with updated files."; } @Override - public String getLeftPanelTitle(VirtualFile file) { + public String getLeftPanelTitle(@NotNull VirtualFile file) { return getConflictLeftPanelTitle(); } @Override - public String getRightPanelTitle(VirtualFile file, VcsRevisionNumber lastRevisionNumber) { + public String getRightPanelTitle(@NotNull VirtualFile file, VcsRevisionNumber revisionNumber) { return getConflictRightPanelTitle(); } } diff --git a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java index ec5c56b37347..9be52ed5cf57 100644 --- a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java +++ b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java @@ -444,17 +444,17 @@ public class GitUnstashDialog extends DialogWrapper { } @Override - public String getMultipleFileMergeDescription(Collection files) { + public String getMultipleFileMergeDescription(@NotNull Collection files) { return "Conflicts during unstashing " + myStashInfo.getStash() + "\"" + myStashInfo.getMessage() + "\""; } @Override - public String getLeftPanelTitle(VirtualFile file) { + public String getLeftPanelTitle(@NotNull VirtualFile file) { return "Local changes"; } @Override - public String getRightPanelTitle(VirtualFile file, VcsRevisionNumber lastRevisionNumber) { + public String getRightPanelTitle(@NotNull VirtualFile file, VcsRevisionNumber revisionNumber) { return "Changes from stash"; } } diff --git a/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java b/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java index bec4da6cd0cf..93e472baae2b 100644 --- a/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java +++ b/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java @@ -125,19 +125,19 @@ public class GitPreservingProcess { GitChangesSaver saver = GitChangesSaver.getSaver(myProject, myFacade, myGit, myProgressIndicator, myStashMessage, saveMethod); MergeDialogCustomizer mergeDialogCustomizer = new MergeDialogCustomizer() { @Override - public String getMultipleFileMergeDescription(Collection files) { + public String getMultipleFileMergeDescription(@NotNull Collection files) { return String.format( "Uncommitted changes that were saved before %s have conflicts with files from %s", myOperationTitle, myDestinationName); } @Override - public String getLeftPanelTitle(VirtualFile file) { + public String getLeftPanelTitle(@NotNull VirtualFile file) { return "Uncommitted changes from stash"; } @Override - public String getRightPanelTitle(VirtualFile file, VcsRevisionNumber lastRevisionNumber) { + public String getRightPanelTitle(@NotNull VirtualFile file, VcsRevisionNumber revisionNumber) { return String.format("Changes from %s", myDestinationName); } }; diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubShowCommitInBrowserFromAnnotateAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubShowCommitInBrowserFromAnnotateAction.java index ed8de64b39b7..4700d3b8b174 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubShowCommitInBrowserFromAnnotateAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubShowCommitInBrowserFromAnnotateAction.java @@ -51,8 +51,7 @@ public class GithubShowCommitInBrowserFromAnnotateAction extends GithubShowCommi e.getPresentation().setVisible(false); return; } - int corrected = eventData.getCorrectedLineNumber(); - e.getPresentation().setEnabled(corrected >= 0 && myAnnotation.getLineRevisionNumber(corrected) != null); + e.getPresentation().setEnabled(myLineNumber >= 0 && myAnnotation.getLineRevisionNumber(myLineNumber) != null); e.getPresentation().setVisible(GithubUtil.isRepositoryOnGitHub(eventData.getRepository())); } @@ -63,7 +62,7 @@ public class GithubShowCommitInBrowserFromAnnotateAction extends GithubShowCommi return; } - final VcsRevisionNumber revisionNumber = myAnnotation.getLineRevisionNumber(eventData.getCorrectedLineNumber()); + final VcsRevisionNumber revisionNumber = myAnnotation.getLineRevisionNumber(myLineNumber); if (revisionNumber != null) { openInBrowser(eventData.getProject(), eventData.getRepository(), revisionNumber.asString()); } @@ -86,7 +85,7 @@ public class GithubShowCommitInBrowserFromAnnotateAction extends GithubShowCommi return null; } - return new EventData(project, repository, lineNumber); + return new EventData(project, repository); } @Override @@ -97,12 +96,10 @@ public class GithubShowCommitInBrowserFromAnnotateAction extends GithubShowCommi private static class EventData { @NotNull private final Project myProject; @NotNull private final GitRepository myRepository; - private final int myCorrectedLineNumber; - private EventData(@NotNull Project project, @NotNull GitRepository repository, int correctedLineNumber) { + private EventData(@NotNull Project project, @NotNull GitRepository repository) { myProject = project; myRepository = repository; - myCorrectedLineNumber = correctedLineNumber; } @NotNull @@ -114,10 +111,5 @@ public class GithubShowCommitInBrowserFromAnnotateAction extends GithubShowCommi public GitRepository getRepository() { return myRepository; } - - private int getCorrectedLineNumber() { - return myCorrectedLineNumber; - } } - } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/source/impl/GroovyTreeGenerator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/source/impl/GroovyTreeGenerator.java index c1a08cc3a593..97d48f1229fc 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/source/impl/GroovyTreeGenerator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/source/impl/GroovyTreeGenerator.java @@ -20,12 +20,13 @@ import com.intellij.psi.PsiManager; import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.impl.source.tree.TreeGenerator; import com.intellij.util.CharTable; +import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; public class GroovyTreeGenerator implements TreeGenerator { @Override - public TreeElement generateTreeFor(PsiElement original, CharTable table, PsiManager manager) { + public TreeElement generateTreeFor(@NotNull PsiElement original, @NotNull CharTable table, @NotNull PsiManager manager) { if (original instanceof GrModifierList) { final String text = original.getText(); diff --git a/python/helpers/docutils/__init__.py b/python/helpers/py2only/docutils/__init__.py similarity index 100% rename from python/helpers/docutils/__init__.py rename to python/helpers/py2only/docutils/__init__.py diff --git a/python/helpers/docutils/_compat.py b/python/helpers/py2only/docutils/_compat.py similarity index 96% rename from python/helpers/docutils/_compat.py rename to python/helpers/py2only/docutils/_compat.py index 87f46da972ce..518f8961650c 100644 --- a/python/helpers/docutils/_compat.py +++ b/python/helpers/py2only/docutils/_compat.py @@ -19,7 +19,6 @@ import sys if sys.version_info < (3,0): b = bytes = str u_prefix = 'u' - from StringIO import StringIO as BytesIO else: import builtins bytes = builtins.bytes diff --git a/python/helpers/docutils/_string_template_compat.py b/python/helpers/py2only/docutils/_string_template_compat.py similarity index 100% rename from python/helpers/docutils/_string_template_compat.py rename to python/helpers/py2only/docutils/_string_template_compat.py diff --git a/python/helpers/docutils/core.py b/python/helpers/py2only/docutils/core.py similarity index 99% rename from python/helpers/docutils/core.py rename to python/helpers/py2only/docutils/core.py index 68928fb20076..5d3d46aea31b 100644 --- a/python/helpers/docutils/core.py +++ b/python/helpers/py2only/docutils/core.py @@ -14,13 +14,13 @@ custom component objects first, and pass *them* to __docformat__ = 'reStructuredText' -import sys import pprint -from docutils import __version__, __version_details__, SettingsSpec -from docutils import frontend, io, utils, readers, writers -from docutils.frontend import OptionParser -from docutils.transforms import Transformer +import sys + import docutils.readers.doctree +from docutils import __version__, __version_details__, SettingsSpec +from docutils import io, utils, readers, writers +from docutils.frontend import OptionParser class Publisher: diff --git a/python/helpers/docutils/docutils.conf b/python/helpers/py2only/docutils/docutils.conf similarity index 100% rename from python/helpers/docutils/docutils.conf rename to python/helpers/py2only/docutils/docutils.conf diff --git a/python/helpers/docutils/examples.py b/python/helpers/py2only/docutils/examples.py similarity index 100% rename from python/helpers/docutils/examples.py rename to python/helpers/py2only/docutils/examples.py diff --git a/python/helpers/docutils/frontend.py b/python/helpers/py2only/docutils/frontend.py similarity index 99% rename from python/helpers/docutils/frontend.py rename to python/helpers/py2only/docutils/frontend.py index f274150205ed..9eadd2278f37 100644 --- a/python/helpers/docutils/frontend.py +++ b/python/helpers/py2only/docutils/frontend.py @@ -27,18 +27,19 @@ Also exports the following functions: __docformat__ = 'reStructuredText' +import ConfigParser as CP +import codecs +import optparse import os import os.path import sys import warnings -import ConfigParser as CP -import codecs -import docutils -import docutils.utils -import docutils.nodes -import optparse from optparse import SUPPRESS_HELP +import docutils +import docutils.nodes +import docutils.utils + def store_multiple(option, opt, value, parser, *args, **kwargs): """ diff --git a/python/helpers/docutils/io.py b/python/helpers/py2only/docutils/io.py similarity index 100% rename from python/helpers/docutils/io.py rename to python/helpers/py2only/docutils/io.py diff --git a/python/helpers/docutils/languages/__init__.py b/python/helpers/py2only/docutils/languages/__init__.py similarity index 100% rename from python/helpers/docutils/languages/__init__.py rename to python/helpers/py2only/docutils/languages/__init__.py diff --git a/python/helpers/docutils/languages/en.py b/python/helpers/py2only/docutils/languages/en.py similarity index 100% rename from python/helpers/docutils/languages/en.py rename to python/helpers/py2only/docutils/languages/en.py diff --git a/python/helpers/docutils/nodes.py b/python/helpers/py2only/docutils/nodes.py similarity index 99% rename from python/helpers/docutils/nodes.py rename to python/helpers/py2only/docutils/nodes.py index c43fa3de8649..cdebc29f3b26 100644 --- a/python/helpers/docutils/nodes.py +++ b/python/helpers/py2only/docutils/nodes.py @@ -21,12 +21,12 @@ hierarchy. __docformat__ = 'reStructuredText' -import sys -import os import re -import warnings +import sys import types import unicodedata +import warnings + # ============================== # Functional Node Base Classes diff --git a/python/helpers/docutils/parsers/__init__.py b/python/helpers/py2only/docutils/parsers/__init__.py similarity index 100% rename from python/helpers/docutils/parsers/__init__.py rename to python/helpers/py2only/docutils/parsers/__init__.py diff --git a/python/helpers/docutils/parsers/null.py b/python/helpers/py2only/docutils/parsers/null.py similarity index 100% rename from python/helpers/docutils/parsers/null.py rename to python/helpers/py2only/docutils/parsers/null.py diff --git a/python/helpers/docutils/parsers/rst/__init__.py b/python/helpers/py2only/docutils/parsers/rst/__init__.py similarity index 99% rename from python/helpers/docutils/parsers/rst/__init__.py rename to python/helpers/py2only/docutils/parsers/rst/__init__.py index de8e2ab5ff56..afcee7965ffc 100644 --- a/python/helpers/docutils/parsers/rst/__init__.py +++ b/python/helpers/py2only/docutils/parsers/rst/__init__.py @@ -69,11 +69,10 @@ appropriate). __docformat__ = 'reStructuredText' - import docutils.parsers import docutils.statemachine +from docutils import frontend from docutils.parsers.rst import states -from docutils import frontend, nodes class Parser(docutils.parsers.Parser): diff --git a/python/helpers/docutils/parsers/rst/directives/__init__.py b/python/helpers/py2only/docutils/parsers/rst/directives/__init__.py similarity index 100% rename from python/helpers/docutils/parsers/rst/directives/__init__.py rename to python/helpers/py2only/docutils/parsers/rst/directives/__init__.py index 8ab69998e385..481456e7cd69 100644 --- a/python/helpers/docutils/parsers/rst/directives/__init__.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/__init__.py @@ -8,12 +8,12 @@ This package contains directive implementation modules. __docformat__ = 'reStructuredText' -import re import codecs +import re + from docutils import nodes from docutils.parsers.rst.languages import en as _fallback_language_module - _directive_registry = { 'attention': ('admonitions', 'Attention'), 'caution': ('admonitions', 'Caution'), diff --git a/python/helpers/docutils/parsers/rst/directives/admonitions.py b/python/helpers/py2only/docutils/parsers/rst/directives/admonitions.py similarity index 97% rename from python/helpers/docutils/parsers/rst/directives/admonitions.py rename to python/helpers/py2only/docutils/parsers/rst/directives/admonitions.py index e7ba007921af..1ab8097a6a36 100644 --- a/python/helpers/docutils/parsers/rst/directives/admonitions.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/admonitions.py @@ -8,10 +8,9 @@ Admonition directives. __docformat__ = 'reStructuredText' - -from docutils.parsers.rst import Directive -from docutils.parsers.rst import states, directives from docutils import nodes +from docutils.parsers.rst import Directive +from docutils.parsers.rst import directives class BaseAdmonition(Directive): diff --git a/python/helpers/docutils/parsers/rst/directives/body.py b/python/helpers/py2only/docutils/parsers/rst/directives/body.py similarity index 99% rename from python/helpers/docutils/parsers/rst/directives/body.py rename to python/helpers/py2only/docutils/parsers/rst/directives/body.py index 13fc06c7b09d..0946d7f935be 100644 --- a/python/helpers/docutils/parsers/rst/directives/body.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/body.py @@ -10,8 +10,6 @@ See `docutils.parsers.rst.directives` for API details. __docformat__ = 'reStructuredText' - -import sys from docutils import nodes from docutils.parsers.rst import Directive from docutils.parsers.rst import directives diff --git a/python/helpers/docutils/parsers/rst/directives/html.py b/python/helpers/py2only/docutils/parsers/rst/directives/html.py similarity index 99% rename from python/helpers/docutils/parsers/rst/directives/html.py rename to python/helpers/py2only/docutils/parsers/rst/directives/html.py index 97dfdadf9c43..6128a026611e 100644 --- a/python/helpers/docutils/parsers/rst/directives/html.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/html.py @@ -8,7 +8,6 @@ Directives for typically HTML-specific constructs. __docformat__ = 'reStructuredText' -import sys from docutils import nodes, utils from docutils.parsers.rst import Directive from docutils.parsers.rst import states diff --git a/python/helpers/docutils/parsers/rst/directives/images.py b/python/helpers/py2only/docutils/parsers/rst/directives/images.py similarity index 99% rename from python/helpers/docutils/parsers/rst/directives/images.py rename to python/helpers/py2only/docutils/parsers/rst/directives/images.py index 6356102de3d2..7fe576f05b95 100644 --- a/python/helpers/docutils/parsers/rst/directives/images.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/images.py @@ -8,12 +8,10 @@ Directives for figures and simple images. __docformat__ = 'reStructuredText' - -import sys -from docutils import nodes, utils +from docutils import nodes +from docutils.nodes import fully_normalize_name, whitespace_normalize_name from docutils.parsers.rst import Directive from docutils.parsers.rst import directives, states -from docutils.nodes import fully_normalize_name, whitespace_normalize_name from docutils.parsers.rst.roles import set_classes try: diff --git a/python/helpers/docutils/parsers/rst/directives/misc.py b/python/helpers/py2only/docutils/parsers/rst/directives/misc.py similarity index 99% rename from python/helpers/docutils/parsers/rst/directives/misc.py rename to python/helpers/py2only/docutils/parsers/rst/directives/misc.py index 63afc7556850..2907a559ee87 100644 --- a/python/helpers/docutils/parsers/rst/directives/misc.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/misc.py @@ -6,15 +6,16 @@ __docformat__ = 'reStructuredText' -import sys import os.path import re import time + from docutils import io, nodes, statemachine, utils from docutils.parsers.rst import Directive, convert_directive_function from docutils.parsers.rst import directives, roles, states from docutils.transforms import misc + class Include(Directive): """ diff --git a/python/helpers/docutils/parsers/rst/directives/parts.py b/python/helpers/py2only/docutils/parsers/rst/directives/parts.py similarity index 100% rename from python/helpers/docutils/parsers/rst/directives/parts.py rename to python/helpers/py2only/docutils/parsers/rst/directives/parts.py index 425ddc714f19..b723049d48b4 100644 --- a/python/helpers/docutils/parsers/rst/directives/parts.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/parts.py @@ -9,9 +9,9 @@ Directives for document parts. __docformat__ = 'reStructuredText' from docutils import nodes, languages -from docutils.transforms import parts from docutils.parsers.rst import Directive from docutils.parsers.rst import directives +from docutils.transforms import parts class Contents(Directive): diff --git a/python/helpers/docutils/parsers/rst/directives/references.py b/python/helpers/py2only/docutils/parsers/rst/directives/references.py similarity index 100% rename from python/helpers/docutils/parsers/rst/directives/references.py rename to python/helpers/py2only/docutils/parsers/rst/directives/references.py index b77f9c5ae800..e62dc534a270 100644 --- a/python/helpers/docutils/parsers/rst/directives/references.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/references.py @@ -9,9 +9,9 @@ Directives for references and targets. __docformat__ = 'reStructuredText' from docutils import nodes -from docutils.transforms import references from docutils.parsers.rst import Directive from docutils.parsers.rst import directives +from docutils.transforms import references class TargetNotes(Directive): diff --git a/python/helpers/docutils/parsers/rst/directives/tables.py b/python/helpers/py2only/docutils/parsers/rst/directives/tables.py similarity index 99% rename from python/helpers/docutils/parsers/rst/directives/tables.py rename to python/helpers/py2only/docutils/parsers/rst/directives/tables.py index a2242fda4674..35e71bdf2cc6 100644 --- a/python/helpers/docutils/parsers/rst/directives/tables.py +++ b/python/helpers/py2only/docutils/parsers/rst/directives/tables.py @@ -8,15 +8,14 @@ Directives for table elements. __docformat__ = 'reStructuredText' - -import sys -import os.path import csv +import os.path +import sys from docutils import io, nodes, statemachine, utils -from docutils.utils import SystemMessagePropagation from docutils.parsers.rst import Directive from docutils.parsers.rst import directives +from docutils.utils import SystemMessagePropagation class Table(Directive): diff --git a/python/helpers/docutils/parsers/rst/include/README.txt b/python/helpers/py2only/docutils/parsers/rst/include/README.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/README.txt rename to python/helpers/py2only/docutils/parsers/rst/include/README.txt diff --git a/python/helpers/docutils/parsers/rst/include/isoamsa.txt b/python/helpers/py2only/docutils/parsers/rst/include/isoamsa.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isoamsa.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isoamsa.txt diff --git a/python/helpers/docutils/parsers/rst/include/isoamsb.txt b/python/helpers/py2only/docutils/parsers/rst/include/isoamsb.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isoamsb.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isoamsb.txt diff --git a/python/helpers/docutils/parsers/rst/include/isoamsc.txt b/python/helpers/py2only/docutils/parsers/rst/include/isoamsc.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isoamsc.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isoamsc.txt diff --git a/python/helpers/docutils/parsers/rst/include/isoamsn.txt b/python/helpers/py2only/docutils/parsers/rst/include/isoamsn.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isoamsn.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isoamsn.txt diff --git a/python/helpers/docutils/parsers/rst/include/isoamso.txt b/python/helpers/py2only/docutils/parsers/rst/include/isoamso.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isoamso.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isoamso.txt diff --git a/python/helpers/docutils/parsers/rst/include/isoamsr.txt b/python/helpers/py2only/docutils/parsers/rst/include/isoamsr.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isoamsr.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isoamsr.txt diff --git a/python/helpers/docutils/parsers/rst/include/isobox.txt b/python/helpers/py2only/docutils/parsers/rst/include/isobox.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isobox.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isobox.txt diff --git a/python/helpers/docutils/parsers/rst/include/isocyr1.txt b/python/helpers/py2only/docutils/parsers/rst/include/isocyr1.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isocyr1.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isocyr1.txt diff --git a/python/helpers/docutils/parsers/rst/include/isocyr2.txt b/python/helpers/py2only/docutils/parsers/rst/include/isocyr2.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isocyr2.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isocyr2.txt diff --git a/python/helpers/docutils/parsers/rst/include/isodia.txt b/python/helpers/py2only/docutils/parsers/rst/include/isodia.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isodia.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isodia.txt diff --git a/python/helpers/docutils/parsers/rst/include/isogrk1.txt b/python/helpers/py2only/docutils/parsers/rst/include/isogrk1.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isogrk1.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isogrk1.txt diff --git a/python/helpers/docutils/parsers/rst/include/isogrk2.txt b/python/helpers/py2only/docutils/parsers/rst/include/isogrk2.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isogrk2.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isogrk2.txt diff --git a/python/helpers/docutils/parsers/rst/include/isogrk3.txt b/python/helpers/py2only/docutils/parsers/rst/include/isogrk3.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isogrk3.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isogrk3.txt diff --git a/python/helpers/docutils/parsers/rst/include/isogrk4-wide.txt b/python/helpers/py2only/docutils/parsers/rst/include/isogrk4-wide.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isogrk4-wide.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isogrk4-wide.txt diff --git a/python/helpers/docutils/parsers/rst/include/isogrk4.txt b/python/helpers/py2only/docutils/parsers/rst/include/isogrk4.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isogrk4.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isogrk4.txt diff --git a/python/helpers/docutils/parsers/rst/include/isolat1.txt b/python/helpers/py2only/docutils/parsers/rst/include/isolat1.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isolat1.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isolat1.txt diff --git a/python/helpers/docutils/parsers/rst/include/isolat2.txt b/python/helpers/py2only/docutils/parsers/rst/include/isolat2.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isolat2.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isolat2.txt diff --git a/python/helpers/docutils/parsers/rst/include/isomfrk-wide.txt b/python/helpers/py2only/docutils/parsers/rst/include/isomfrk-wide.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isomfrk-wide.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isomfrk-wide.txt diff --git a/python/helpers/docutils/parsers/rst/include/isomfrk.txt b/python/helpers/py2only/docutils/parsers/rst/include/isomfrk.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isomfrk.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isomfrk.txt diff --git a/python/helpers/docutils/parsers/rst/include/isomopf-wide.txt b/python/helpers/py2only/docutils/parsers/rst/include/isomopf-wide.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isomopf-wide.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isomopf-wide.txt diff --git a/python/helpers/docutils/parsers/rst/include/isomopf.txt b/python/helpers/py2only/docutils/parsers/rst/include/isomopf.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isomopf.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isomopf.txt diff --git a/python/helpers/docutils/parsers/rst/include/isomscr-wide.txt b/python/helpers/py2only/docutils/parsers/rst/include/isomscr-wide.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isomscr-wide.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isomscr-wide.txt diff --git a/python/helpers/docutils/parsers/rst/include/isomscr.txt b/python/helpers/py2only/docutils/parsers/rst/include/isomscr.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isomscr.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isomscr.txt diff --git a/python/helpers/docutils/parsers/rst/include/isonum.txt b/python/helpers/py2only/docutils/parsers/rst/include/isonum.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isonum.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isonum.txt diff --git a/python/helpers/docutils/parsers/rst/include/isopub.txt b/python/helpers/py2only/docutils/parsers/rst/include/isopub.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isopub.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isopub.txt diff --git a/python/helpers/docutils/parsers/rst/include/isotech.txt b/python/helpers/py2only/docutils/parsers/rst/include/isotech.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/isotech.txt rename to python/helpers/py2only/docutils/parsers/rst/include/isotech.txt diff --git a/python/helpers/docutils/parsers/rst/include/mmlalias.txt b/python/helpers/py2only/docutils/parsers/rst/include/mmlalias.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/mmlalias.txt rename to python/helpers/py2only/docutils/parsers/rst/include/mmlalias.txt diff --git a/python/helpers/docutils/parsers/rst/include/mmlextra-wide.txt b/python/helpers/py2only/docutils/parsers/rst/include/mmlextra-wide.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/mmlextra-wide.txt rename to python/helpers/py2only/docutils/parsers/rst/include/mmlextra-wide.txt diff --git a/python/helpers/docutils/parsers/rst/include/mmlextra.txt b/python/helpers/py2only/docutils/parsers/rst/include/mmlextra.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/mmlextra.txt rename to python/helpers/py2only/docutils/parsers/rst/include/mmlextra.txt diff --git a/python/helpers/docutils/parsers/rst/include/s5defs.txt b/python/helpers/py2only/docutils/parsers/rst/include/s5defs.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/s5defs.txt rename to python/helpers/py2only/docutils/parsers/rst/include/s5defs.txt diff --git a/python/helpers/docutils/parsers/rst/include/xhtml1-lat1.txt b/python/helpers/py2only/docutils/parsers/rst/include/xhtml1-lat1.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/xhtml1-lat1.txt rename to python/helpers/py2only/docutils/parsers/rst/include/xhtml1-lat1.txt diff --git a/python/helpers/docutils/parsers/rst/include/xhtml1-special.txt b/python/helpers/py2only/docutils/parsers/rst/include/xhtml1-special.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/xhtml1-special.txt rename to python/helpers/py2only/docutils/parsers/rst/include/xhtml1-special.txt diff --git a/python/helpers/docutils/parsers/rst/include/xhtml1-symbol.txt b/python/helpers/py2only/docutils/parsers/rst/include/xhtml1-symbol.txt similarity index 100% rename from python/helpers/docutils/parsers/rst/include/xhtml1-symbol.txt rename to python/helpers/py2only/docutils/parsers/rst/include/xhtml1-symbol.txt diff --git a/python/helpers/docutils/parsers/rst/languages/__init__.py b/python/helpers/py2only/docutils/parsers/rst/languages/__init__.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/__init__.py rename to python/helpers/py2only/docutils/parsers/rst/languages/__init__.py diff --git a/python/helpers/docutils/parsers/rst/languages/af.py b/python/helpers/py2only/docutils/parsers/rst/languages/af.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/af.py rename to python/helpers/py2only/docutils/parsers/rst/languages/af.py diff --git a/python/helpers/docutils/parsers/rst/languages/ca.py b/python/helpers/py2only/docutils/parsers/rst/languages/ca.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/ca.py rename to python/helpers/py2only/docutils/parsers/rst/languages/ca.py diff --git a/python/helpers/docutils/parsers/rst/languages/cs.py b/python/helpers/py2only/docutils/parsers/rst/languages/cs.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/cs.py rename to python/helpers/py2only/docutils/parsers/rst/languages/cs.py diff --git a/python/helpers/docutils/parsers/rst/languages/de.py b/python/helpers/py2only/docutils/parsers/rst/languages/de.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/de.py rename to python/helpers/py2only/docutils/parsers/rst/languages/de.py diff --git a/python/helpers/docutils/parsers/rst/languages/en.py b/python/helpers/py2only/docutils/parsers/rst/languages/en.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/en.py rename to python/helpers/py2only/docutils/parsers/rst/languages/en.py diff --git a/python/helpers/docutils/parsers/rst/languages/eo.py b/python/helpers/py2only/docutils/parsers/rst/languages/eo.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/eo.py rename to python/helpers/py2only/docutils/parsers/rst/languages/eo.py diff --git a/python/helpers/docutils/parsers/rst/languages/es.py b/python/helpers/py2only/docutils/parsers/rst/languages/es.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/es.py rename to python/helpers/py2only/docutils/parsers/rst/languages/es.py diff --git a/python/helpers/docutils/parsers/rst/languages/fi.py b/python/helpers/py2only/docutils/parsers/rst/languages/fi.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/fi.py rename to python/helpers/py2only/docutils/parsers/rst/languages/fi.py diff --git a/python/helpers/docutils/parsers/rst/languages/fr.py b/python/helpers/py2only/docutils/parsers/rst/languages/fr.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/fr.py rename to python/helpers/py2only/docutils/parsers/rst/languages/fr.py diff --git a/python/helpers/docutils/parsers/rst/languages/gl.py b/python/helpers/py2only/docutils/parsers/rst/languages/gl.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/gl.py rename to python/helpers/py2only/docutils/parsers/rst/languages/gl.py diff --git a/python/helpers/docutils/parsers/rst/languages/he.py b/python/helpers/py2only/docutils/parsers/rst/languages/he.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/he.py rename to python/helpers/py2only/docutils/parsers/rst/languages/he.py diff --git a/python/helpers/docutils/parsers/rst/languages/it.py b/python/helpers/py2only/docutils/parsers/rst/languages/it.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/it.py rename to python/helpers/py2only/docutils/parsers/rst/languages/it.py diff --git a/python/helpers/docutils/parsers/rst/languages/ja.py b/python/helpers/py2only/docutils/parsers/rst/languages/ja.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/ja.py rename to python/helpers/py2only/docutils/parsers/rst/languages/ja.py diff --git a/python/helpers/docutils/parsers/rst/languages/nl.py b/python/helpers/py2only/docutils/parsers/rst/languages/nl.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/nl.py rename to python/helpers/py2only/docutils/parsers/rst/languages/nl.py diff --git a/python/helpers/docutils/parsers/rst/languages/pl.py b/python/helpers/py2only/docutils/parsers/rst/languages/pl.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/pl.py rename to python/helpers/py2only/docutils/parsers/rst/languages/pl.py diff --git a/python/helpers/docutils/parsers/rst/languages/pt_br.py b/python/helpers/py2only/docutils/parsers/rst/languages/pt_br.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/pt_br.py rename to python/helpers/py2only/docutils/parsers/rst/languages/pt_br.py diff --git a/python/helpers/docutils/parsers/rst/languages/ru.py b/python/helpers/py2only/docutils/parsers/rst/languages/ru.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/ru.py rename to python/helpers/py2only/docutils/parsers/rst/languages/ru.py diff --git a/python/helpers/docutils/parsers/rst/languages/sk.py b/python/helpers/py2only/docutils/parsers/rst/languages/sk.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/sk.py rename to python/helpers/py2only/docutils/parsers/rst/languages/sk.py diff --git a/python/helpers/docutils/parsers/rst/languages/sv.py b/python/helpers/py2only/docutils/parsers/rst/languages/sv.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/sv.py rename to python/helpers/py2only/docutils/parsers/rst/languages/sv.py diff --git a/python/helpers/docutils/parsers/rst/languages/zh_cn.py b/python/helpers/py2only/docutils/parsers/rst/languages/zh_cn.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/zh_cn.py rename to python/helpers/py2only/docutils/parsers/rst/languages/zh_cn.py diff --git a/python/helpers/docutils/parsers/rst/languages/zh_tw.py b/python/helpers/py2only/docutils/parsers/rst/languages/zh_tw.py similarity index 100% rename from python/helpers/docutils/parsers/rst/languages/zh_tw.py rename to python/helpers/py2only/docutils/parsers/rst/languages/zh_tw.py diff --git a/python/helpers/docutils/parsers/rst/roles.py b/python/helpers/py2only/docutils/parsers/rst/roles.py similarity index 100% rename from python/helpers/docutils/parsers/rst/roles.py rename to python/helpers/py2only/docutils/parsers/rst/roles.py diff --git a/python/helpers/docutils/parsers/rst/states.py b/python/helpers/py2only/docutils/parsers/rst/states.py similarity index 99% rename from python/helpers/docutils/parsers/rst/states.py rename to python/helpers/py2only/docutils/parsers/rst/states.py index f3ad67939443..c88139c58428 100644 --- a/python/helpers/docutils/parsers/rst/states.py +++ b/python/helpers/py2only/docutils/parsers/rst/states.py @@ -102,20 +102,18 @@ Parsing proceeds as follows: __docformat__ = 'reStructuredText' - -import sys import re -import roman from types import FunctionType, MethodType -from docutils import nodes, statemachine, utils, urischemes + +import docutils.parsers.rst +import roman from docutils import ApplicationError, DataError -from docutils.statemachine import StateMachineWS, StateWS +from docutils import nodes, statemachine, utils, urischemes from docutils.nodes import fully_normalize_name as normalize_name from docutils.nodes import whitespace_normalize_name -from docutils.utils import escape2null, unescape, column_width -import docutils.parsers.rst from docutils.parsers.rst import directives, languages, tableparser, roles -from docutils.parsers.rst.languages import en as _fallback_language_module +from docutils.statemachine import StateMachineWS, StateWS +from docutils.utils import escape2null, unescape, column_width class MarkupError(DataError): pass diff --git a/python/helpers/docutils/parsers/rst/tableparser.py b/python/helpers/py2only/docutils/parsers/rst/tableparser.py similarity index 100% rename from python/helpers/docutils/parsers/rst/tableparser.py rename to python/helpers/py2only/docutils/parsers/rst/tableparser.py index 5de124a55309..fafd467bdfb4 100644 --- a/python/helpers/docutils/parsers/rst/tableparser.py +++ b/python/helpers/py2only/docutils/parsers/rst/tableparser.py @@ -19,9 +19,9 @@ and produce a well-formed data structure suitable for building a CALS table. __docformat__ = 'reStructuredText' - import re import sys + from docutils import DataError diff --git a/python/helpers/docutils/readers/__init__.py b/python/helpers/py2only/docutils/readers/__init__.py similarity index 100% rename from python/helpers/docutils/readers/__init__.py rename to python/helpers/py2only/docutils/readers/__init__.py diff --git a/python/helpers/docutils/readers/doctree.py b/python/helpers/py2only/docutils/readers/doctree.py similarity index 100% rename from python/helpers/docutils/readers/doctree.py rename to python/helpers/py2only/docutils/readers/doctree.py diff --git a/python/helpers/docutils/readers/pep.py b/python/helpers/py2only/docutils/readers/pep.py similarity index 95% rename from python/helpers/docutils/readers/pep.py rename to python/helpers/py2only/docutils/readers/pep.py index d6fed72e80ae..a66169bb9431 100644 --- a/python/helpers/docutils/readers/pep.py +++ b/python/helpers/py2only/docutils/readers/pep.py @@ -8,10 +8,9 @@ Python Enhancement Proposal (PEP) Reader. __docformat__ = 'reStructuredText' - -from docutils.readers import standalone -from docutils.transforms import peps, references, misc, frontmatter from docutils.parsers import rst +from docutils.readers import standalone +from docutils.transforms import peps, frontmatter class Reader(standalone.Reader): diff --git a/python/helpers/docutils/readers/python/__init__.py b/python/helpers/py2only/docutils/readers/python/__init__.py similarity index 99% rename from python/helpers/docutils/readers/python/__init__.py rename to python/helpers/py2only/docutils/readers/python/__init__.py index f0461447b8f5..4718d87452cb 100644 --- a/python/helpers/docutils/readers/python/__init__.py +++ b/python/helpers/py2only/docutils/readers/python/__init__.py @@ -8,14 +8,12 @@ This package contains the Python Source Reader modules. __docformat__ = 'reStructuredText' - -import sys import docutils.readers -from docutils.readers.python import moduleparser -from docutils import parsers from docutils import nodes +from docutils import parsers +from docutils.readers.python import moduleparser from docutils.readers.python import pynodes -from docutils import readers + class Reader(docutils.readers.Reader): diff --git a/python/helpers/docutils/readers/python/moduleparser.py b/python/helpers/py2only/docutils/readers/python/moduleparser.py similarity index 99% rename from python/helpers/docutils/readers/python/moduleparser.py rename to python/helpers/py2only/docutils/readers/python/moduleparser.py index 182327273685..f0275c44b4b9 100644 --- a/python/helpers/docutils/readers/python/moduleparser.py +++ b/python/helpers/py2only/docutils/readers/python/moduleparser.py @@ -207,15 +207,14 @@ Issues __docformat__ = 'reStructuredText' -import sys import compiler import compiler.ast -import tokenize import token -from compiler.consts import OP_ASSIGN +import tokenize from compiler.visitor import ASTVisitor -from docutils.readers.python import pynodes + from docutils.nodes import Text +from docutils.readers.python import pynodes def parse_module(module_text, filename): diff --git a/python/helpers/docutils/readers/python/pynodes.py b/python/helpers/py2only/docutils/readers/python/pynodes.py similarity index 97% rename from python/helpers/docutils/readers/python/pynodes.py rename to python/helpers/py2only/docutils/readers/python/pynodes.py index e90314dc230c..4b69be849187 100644 --- a/python/helpers/docutils/readers/python/pynodes.py +++ b/python/helpers/py2only/docutils/readers/python/pynodes.py @@ -3,11 +3,12 @@ # Author: David Goodger # Copyright: This module has been placed in the public domain. -from docutils import nodes -from docutils.nodes import Element, TextElement, Structural, Inline, Part, \ - Text import types +from docutils import nodes +from docutils.nodes import Element, TextElement, Structural + + # This is the parent class of all the other pynode classes: class PythonStructural(Structural): pass diff --git a/python/helpers/docutils/readers/standalone.py b/python/helpers/py2only/docutils/readers/standalone.py similarity index 99% rename from python/helpers/docutils/readers/standalone.py rename to python/helpers/py2only/docutils/readers/standalone.py index 3c302ed346c6..27feef64f3f4 100644 --- a/python/helpers/docutils/readers/standalone.py +++ b/python/helpers/py2only/docutils/readers/standalone.py @@ -8,8 +8,6 @@ Standalone file Reader for the reStructuredText markup syntax. __docformat__ = 'reStructuredText' - -import sys from docutils import frontend, readers from docutils.transforms import frontmatter, references, misc diff --git a/python/helpers/docutils/statemachine.py b/python/helpers/py2only/docutils/statemachine.py similarity index 100% rename from python/helpers/docutils/statemachine.py rename to python/helpers/py2only/docutils/statemachine.py index aa9d2c00b38e..18d0592e5a66 100644 --- a/python/helpers/docutils/statemachine.py +++ b/python/helpers/py2only/docutils/statemachine.py @@ -106,8 +106,8 @@ How To Use This Module __docformat__ = 'restructuredtext' -import sys import re +import sys import types import unicodedata diff --git a/python/helpers/docutils/transforms/__init__.py b/python/helpers/py2only/docutils/transforms/__init__.py similarity index 100% rename from python/helpers/docutils/transforms/__init__.py rename to python/helpers/py2only/docutils/transforms/__init__.py diff --git a/python/helpers/docutils/transforms/components.py b/python/helpers/py2only/docutils/transforms/components.py similarity index 90% rename from python/helpers/docutils/transforms/components.py rename to python/helpers/py2only/docutils/transforms/components.py index d3e548c4f205..c90d866a1bcc 100644 --- a/python/helpers/docutils/transforms/components.py +++ b/python/helpers/py2only/docutils/transforms/components.py @@ -8,13 +8,7 @@ Docutils component-related transforms. __docformat__ = 'reStructuredText' -import sys -import os -import re -import time -from docutils import nodes, utils -from docutils import ApplicationError, DataError -from docutils.transforms import Transform, TransformError +from docutils.transforms import Transform class Filter(Transform): diff --git a/python/helpers/docutils/transforms/frontmatter.py b/python/helpers/py2only/docutils/transforms/frontmatter.py similarity index 99% rename from python/helpers/docutils/transforms/frontmatter.py rename to python/helpers/py2only/docutils/transforms/frontmatter.py index 63ae55d2b20b..c4617a3a6b78 100644 --- a/python/helpers/docutils/transforms/frontmatter.py +++ b/python/helpers/py2only/docutils/transforms/frontmatter.py @@ -22,6 +22,7 @@ Transforms related to the front matter of a document or a section __docformat__ = 'reStructuredText' import re + from docutils import nodes, utils from docutils.transforms import TransformError, Transform diff --git a/python/helpers/docutils/transforms/misc.py b/python/helpers/py2only/docutils/transforms/misc.py similarity index 98% rename from python/helpers/docutils/transforms/misc.py rename to python/helpers/py2only/docutils/transforms/misc.py index cd68ee150388..6caa703e06ee 100644 --- a/python/helpers/docutils/transforms/misc.py +++ b/python/helpers/py2only/docutils/transforms/misc.py @@ -9,7 +9,7 @@ Miscellaneous transforms. __docformat__ = 'reStructuredText' from docutils import nodes -from docutils.transforms import Transform, TransformError +from docutils.transforms import Transform class CallBack(Transform): diff --git a/python/helpers/docutils/transforms/parts.py b/python/helpers/py2only/docutils/transforms/parts.py similarity index 98% rename from python/helpers/docutils/transforms/parts.py rename to python/helpers/py2only/docutils/transforms/parts.py index ec6eda373e6b..913eabc8cf99 100644 --- a/python/helpers/docutils/transforms/parts.py +++ b/python/helpers/py2only/docutils/transforms/parts.py @@ -8,11 +8,10 @@ Transforms related to document parts. __docformat__ = 'reStructuredText' - -import re import sys -from docutils import nodes, utils -from docutils.transforms import TransformError, Transform + +from docutils import nodes +from docutils.transforms import Transform class SectNum(Transform): diff --git a/python/helpers/docutils/transforms/peps.py b/python/helpers/py2only/docutils/transforms/peps.py similarity index 98% rename from python/helpers/docutils/transforms/peps.py rename to python/helpers/py2only/docutils/transforms/peps.py index b19248bbe9bc..504d09bf1884 100644 --- a/python/helpers/docutils/transforms/peps.py +++ b/python/helpers/py2only/docutils/transforms/peps.py @@ -13,13 +13,13 @@ Transforms for PEP processing. __docformat__ = 'reStructuredText' -import sys import os import re import time + +from docutils import DataError from docutils import nodes, utils, languages -from docutils import ApplicationError, DataError -from docutils.transforms import Transform, TransformError +from docutils.transforms import Transform from docutils.transforms import parts, references, misc diff --git a/python/helpers/docutils/transforms/references.py b/python/helpers/py2only/docutils/transforms/references.py similarity index 99% rename from python/helpers/docutils/transforms/references.py rename to python/helpers/py2only/docutils/transforms/references.py index 4d8a716df985..ef235b3f99e6 100644 --- a/python/helpers/docutils/transforms/references.py +++ b/python/helpers/py2only/docutils/transforms/references.py @@ -8,10 +8,8 @@ Transforms for resolving references. __docformat__ = 'reStructuredText' -import sys -import re from docutils import nodes, utils -from docutils.transforms import TransformError, Transform +from docutils.transforms import Transform class PropagateTargets(Transform): diff --git a/python/helpers/docutils/transforms/universal.py b/python/helpers/py2only/docutils/transforms/universal.py similarity index 98% rename from python/helpers/docutils/transforms/universal.py rename to python/helpers/py2only/docutils/transforms/universal.py index 0e8f2c72cfee..02c8cc1c4e46 100644 --- a/python/helpers/docutils/transforms/universal.py +++ b/python/helpers/py2only/docutils/transforms/universal.py @@ -14,11 +14,10 @@ Transforms needed by most or all documents: __docformat__ = 'reStructuredText' -import re -import sys import time + from docutils import nodes, utils -from docutils.transforms import TransformError, Transform +from docutils.transforms import Transform class Decorations(Transform): diff --git a/python/helpers/docutils/transforms/writer_aux.py b/python/helpers/py2only/docutils/transforms/writer_aux.py similarity index 98% rename from python/helpers/docutils/transforms/writer_aux.py rename to python/helpers/py2only/docutils/transforms/writer_aux.py index 8045703d57ae..28e0d3a3b0a9 100644 --- a/python/helpers/docutils/transforms/writer_aux.py +++ b/python/helpers/py2only/docutils/transforms/writer_aux.py @@ -14,7 +14,7 @@ conflicting imports like this one:: __docformat__ = 'reStructuredText' -from docutils import nodes, utils, languages +from docutils import nodes, languages from docutils.transforms import Transform diff --git a/python/helpers/docutils/urischemes.py b/python/helpers/py2only/docutils/urischemes.py similarity index 100% rename from python/helpers/docutils/urischemes.py rename to python/helpers/py2only/docutils/urischemes.py diff --git a/python/helpers/docutils/utils.py b/python/helpers/py2only/docutils/utils.py similarity index 99% rename from python/helpers/docutils/utils.py rename to python/helpers/py2only/docutils/utils.py index e5443bd74617..dd0cc3fedccf 100644 --- a/python/helpers/docutils/utils.py +++ b/python/helpers/py2only/docutils/utils.py @@ -8,11 +8,12 @@ Miscellaneous utilities for the documentation utilities. __docformat__ = 'reStructuredText' -import sys import os import os.path -import warnings +import sys import unicodedata +import warnings + from docutils import ApplicationError, DataError from docutils import nodes from docutils._compat import bytes diff --git a/python/helpers/docutils/writers/__init__.py b/python/helpers/py2only/docutils/writers/__init__.py similarity index 99% rename from python/helpers/docutils/writers/__init__.py rename to python/helpers/py2only/docutils/writers/__init__.py index cc80d844cac1..e1a89f4a1146 100644 --- a/python/helpers/docutils/writers/__init__.py +++ b/python/helpers/py2only/docutils/writers/__init__.py @@ -8,8 +8,6 @@ This package contains Docutils Writer modules. __docformat__ = 'reStructuredText' - -import os.path import docutils from docutils import languages, Component from docutils.transforms import universal diff --git a/python/helpers/docutils/writers/docutils_xml.py b/python/helpers/py2only/docutils/writers/docutils_xml.py similarity index 100% rename from python/helpers/docutils/writers/docutils_xml.py rename to python/helpers/py2only/docutils/writers/docutils_xml.py diff --git a/python/helpers/docutils/writers/html4css1/__init__.py b/python/helpers/py2only/docutils/writers/html4css1/__init__.py similarity index 99% rename from python/helpers/docutils/writers/html4css1/__init__.py rename to python/helpers/py2only/docutils/writers/html4css1/__init__.py index c1fa360b7c11..3955f10ff4c5 100644 --- a/python/helpers/docutils/writers/html4css1/__init__.py +++ b/python/helpers/py2only/docutils/writers/html4css1/__init__.py @@ -13,11 +13,8 @@ for proper viewing with a modern graphical browser. __docformat__ = 'reStructuredText' - -import sys import os import os.path -import time import re try: import Image # check for the Python Imaging Library diff --git a/python/helpers/docutils/writers/html4css1/html4css1.css b/python/helpers/py2only/docutils/writers/html4css1/html4css1.css similarity index 100% rename from python/helpers/docutils/writers/html4css1/html4css1.css rename to python/helpers/py2only/docutils/writers/html4css1/html4css1.css diff --git a/python/helpers/docutils/writers/html4css1/template.txt b/python/helpers/py2only/docutils/writers/html4css1/template.txt similarity index 100% rename from python/helpers/docutils/writers/html4css1/template.txt rename to python/helpers/py2only/docutils/writers/html4css1/template.txt diff --git a/python/helpers/docutils/writers/latex2e/__init__.py b/python/helpers/py2only/docutils/writers/latex2e/__init__.py similarity index 99% rename from python/helpers/docutils/writers/latex2e/__init__.py rename to python/helpers/py2only/docutils/writers/latex2e/__init__.py index c8ef32ef6879..b6bd419007f6 100644 --- a/python/helpers/docutils/writers/latex2e/__init__.py +++ b/python/helpers/py2only/docutils/writers/latex2e/__init__.py @@ -12,11 +12,10 @@ __docformat__ = 'reStructuredText' # # convention deactivate code by two # i.e. ##. -import sys import os -import time import re import string + from docutils import frontend, nodes, languages, writers, utils, io from docutils.transforms import writer_aux diff --git a/python/helpers/docutils/writers/latex2e/default.tex b/python/helpers/py2only/docutils/writers/latex2e/default.tex similarity index 100% rename from python/helpers/docutils/writers/latex2e/default.tex rename to python/helpers/py2only/docutils/writers/latex2e/default.tex diff --git a/python/helpers/docutils/writers/latex2e/docutils-05-compat.sty b/python/helpers/py2only/docutils/writers/latex2e/docutils-05-compat.sty similarity index 100% rename from python/helpers/docutils/writers/latex2e/docutils-05-compat.sty rename to python/helpers/py2only/docutils/writers/latex2e/docutils-05-compat.sty diff --git a/python/helpers/docutils/writers/latex2e/titlepage.tex b/python/helpers/py2only/docutils/writers/latex2e/titlepage.tex similarity index 100% rename from python/helpers/docutils/writers/latex2e/titlepage.tex rename to python/helpers/py2only/docutils/writers/latex2e/titlepage.tex diff --git a/python/helpers/docutils/writers/manpage.py b/python/helpers/py2only/docutils/writers/manpage.py similarity index 99% rename from python/helpers/docutils/writers/manpage.py rename to python/helpers/py2only/docutils/writers/manpage.py index e4e46b389219..d2085ea1dbeb 100644 --- a/python/helpers/docutils/writers/manpage.py +++ b/python/helpers/py2only/docutils/writers/manpage.py @@ -46,9 +46,8 @@ __docformat__ = 'reStructuredText' import re -import docutils -from docutils import nodes, writers, languages import roman +from docutils import nodes, writers, languages FIELD_LIST_INDENT = 7 DEFINITION_LIST_INDENT = 7 diff --git a/python/helpers/docutils/writers/newlatex2e/__init__.py b/python/helpers/py2only/docutils/writers/newlatex2e/__init__.py similarity index 99% rename from python/helpers/docutils/writers/newlatex2e/__init__.py rename to python/helpers/py2only/docutils/writers/newlatex2e/__init__.py index c04208df7fe0..e2274612f9bd 100644 --- a/python/helpers/docutils/writers/newlatex2e/__init__.py +++ b/python/helpers/py2only/docutils/writers/newlatex2e/__init__.py @@ -12,14 +12,13 @@ LaTeX2e document tree Writer. __docformat__ = 'reStructuredText' - -import re import os.path +import re import docutils from docutils import nodes, writers, utils -from docutils.writers.newlatex2e import unicode_map from docutils.transforms import writer_aux +from docutils.writers.newlatex2e import unicode_map class Writer(writers.Writer): diff --git a/python/helpers/docutils/writers/newlatex2e/base.tex b/python/helpers/py2only/docutils/writers/newlatex2e/base.tex similarity index 100% rename from python/helpers/docutils/writers/newlatex2e/base.tex rename to python/helpers/py2only/docutils/writers/newlatex2e/base.tex diff --git a/python/helpers/docutils/writers/newlatex2e/notes.txt b/python/helpers/py2only/docutils/writers/newlatex2e/notes.txt similarity index 100% rename from python/helpers/docutils/writers/newlatex2e/notes.txt rename to python/helpers/py2only/docutils/writers/newlatex2e/notes.txt diff --git a/python/helpers/docutils/writers/newlatex2e/tests.txt b/python/helpers/py2only/docutils/writers/newlatex2e/tests.txt similarity index 100% rename from python/helpers/docutils/writers/newlatex2e/tests.txt rename to python/helpers/py2only/docutils/writers/newlatex2e/tests.txt diff --git a/python/helpers/docutils/writers/newlatex2e/unicode_map.py b/python/helpers/py2only/docutils/writers/newlatex2e/unicode_map.py similarity index 100% rename from python/helpers/docutils/writers/newlatex2e/unicode_map.py rename to python/helpers/py2only/docutils/writers/newlatex2e/unicode_map.py diff --git a/python/helpers/docutils/writers/null.py b/python/helpers/py2only/docutils/writers/null.py similarity index 100% rename from python/helpers/docutils/writers/null.py rename to python/helpers/py2only/docutils/writers/null.py diff --git a/python/helpers/docutils/writers/odf_odt/__init__.py b/python/helpers/py2only/docutils/writers/odf_odt/__init__.py similarity index 99% rename from python/helpers/docutils/writers/odf_odt/__init__.py rename to python/helpers/py2only/docutils/writers/odf_odt/__init__.py index f7a41bf7ec7a..5b2d6c688868 100644 --- a/python/helpers/docutils/writers/odf_odt/__init__.py +++ b/python/helpers/py2only/docutils/writers/odf_odt/__init__.py @@ -11,26 +11,21 @@ VERSION = '1.0a' __docformat__ = 'reStructuredText' - -import sys +import StringIO +import copy import os import os.path +import re import tempfile +import time import zipfile from xml.dom import minidom -import time -import re -import StringIO -import inspect -import imp -import copy + import docutils -from docutils import frontend, nodes, utils, writers, languages -from docutils.parsers import rst +from docutils import frontend, nodes, utils, writers from docutils.readers import standalone from docutils.transforms import references - WhichElementTree = '' try: # 1. Try to use lxml. diff --git a/python/helpers/docutils/writers/odf_odt/pygmentsformatter.py b/python/helpers/py2only/docutils/writers/odf_odt/pygmentsformatter.py similarity index 100% rename from python/helpers/docutils/writers/odf_odt/pygmentsformatter.py rename to python/helpers/py2only/docutils/writers/odf_odt/pygmentsformatter.py diff --git a/python/helpers/docutils/writers/odf_odt/styles.odt b/python/helpers/py2only/docutils/writers/odf_odt/styles.odt similarity index 100% rename from python/helpers/docutils/writers/odf_odt/styles.odt rename to python/helpers/py2only/docutils/writers/odf_odt/styles.odt diff --git a/python/helpers/docutils/writers/pep_html/__init__.py b/python/helpers/py2only/docutils/writers/pep_html/__init__.py similarity index 97% rename from python/helpers/docutils/writers/pep_html/__init__.py rename to python/helpers/py2only/docutils/writers/pep_html/__init__.py index 503fa17bfe6f..e4776dbb2886 100644 --- a/python/helpers/docutils/writers/pep_html/__init__.py +++ b/python/helpers/py2only/docutils/writers/pep_html/__init__.py @@ -8,13 +8,10 @@ PEP HTML Writer. __docformat__ = 'reStructuredText' - -import sys import os import os.path -import codecs -import docutils -from docutils import frontend, nodes, utils, writers + +from docutils import frontend, nodes, utils from docutils.writers import html4css1 diff --git a/python/helpers/docutils/writers/pep_html/pep.css b/python/helpers/py2only/docutils/writers/pep_html/pep.css similarity index 100% rename from python/helpers/docutils/writers/pep_html/pep.css rename to python/helpers/py2only/docutils/writers/pep_html/pep.css diff --git a/python/helpers/docutils/writers/pep_html/template.txt b/python/helpers/py2only/docutils/writers/pep_html/template.txt similarity index 100% rename from python/helpers/docutils/writers/pep_html/template.txt rename to python/helpers/py2only/docutils/writers/pep_html/template.txt diff --git a/python/helpers/docutils/writers/pseudoxml.py b/python/helpers/py2only/docutils/writers/pseudoxml.py similarity index 100% rename from python/helpers/docutils/writers/pseudoxml.py rename to python/helpers/py2only/docutils/writers/pseudoxml.py diff --git a/python/helpers/docutils/writers/s5_html/__init__.py b/python/helpers/py2only/docutils/writers/s5_html/__init__.py similarity index 99% rename from python/helpers/docutils/writers/s5_html/__init__.py rename to python/helpers/py2only/docutils/writers/s5_html/__init__.py index 12647bc78353..9e641f6e504e 100644 --- a/python/helpers/docutils/writers/s5_html/__init__.py +++ b/python/helpers/py2only/docutils/writers/s5_html/__init__.py @@ -9,15 +9,14 @@ S5/HTML Slideshow Writer. __docformat__ = 'reStructuredText' - -import sys import os import re +import sys + import docutils from docutils import frontend, nodes, utils -from docutils.writers import html4css1 -from docutils.parsers.rst import directives from docutils._compat import b +from docutils.writers import html4css1 themes_dir_path = utils.relative_path( os.path.join(os.getcwd(), 'dummy'), diff --git a/python/helpers/docutils/writers/s5_html/themes/README.txt b/python/helpers/py2only/docutils/writers/s5_html/themes/README.txt similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/README.txt rename to python/helpers/py2only/docutils/writers/s5_html/themes/README.txt diff --git a/python/helpers/docutils/writers/s5_html/themes/big-black/__base__ b/python/helpers/py2only/docutils/writers/s5_html/themes/big-black/__base__ similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/big-black/__base__ rename to python/helpers/py2only/docutils/writers/s5_html/themes/big-black/__base__ diff --git a/python/helpers/docutils/writers/s5_html/themes/big-black/framing.css b/python/helpers/py2only/docutils/writers/s5_html/themes/big-black/framing.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/big-black/framing.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/big-black/framing.css diff --git a/python/helpers/docutils/writers/s5_html/themes/big-black/pretty.css b/python/helpers/py2only/docutils/writers/s5_html/themes/big-black/pretty.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/big-black/pretty.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/big-black/pretty.css diff --git a/python/helpers/docutils/writers/s5_html/themes/big-white/framing.css b/python/helpers/py2only/docutils/writers/s5_html/themes/big-white/framing.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/big-white/framing.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/big-white/framing.css diff --git a/python/helpers/docutils/writers/s5_html/themes/big-white/pretty.css b/python/helpers/py2only/docutils/writers/s5_html/themes/big-white/pretty.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/big-white/pretty.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/big-white/pretty.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/blank.gif b/python/helpers/py2only/docutils/writers/s5_html/themes/default/blank.gif similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/blank.gif rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/blank.gif diff --git a/python/helpers/docutils/writers/s5_html/themes/default/framing.css b/python/helpers/py2only/docutils/writers/s5_html/themes/default/framing.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/framing.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/framing.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/iepngfix.htc b/python/helpers/py2only/docutils/writers/s5_html/themes/default/iepngfix.htc similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/iepngfix.htc rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/iepngfix.htc diff --git a/python/helpers/docutils/writers/s5_html/themes/default/opera.css b/python/helpers/py2only/docutils/writers/s5_html/themes/default/opera.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/opera.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/opera.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/outline.css b/python/helpers/py2only/docutils/writers/s5_html/themes/default/outline.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/outline.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/outline.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/pretty.css b/python/helpers/py2only/docutils/writers/s5_html/themes/default/pretty.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/pretty.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/pretty.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/print.css b/python/helpers/py2only/docutils/writers/s5_html/themes/default/print.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/print.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/print.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/s5-core.css b/python/helpers/py2only/docutils/writers/s5_html/themes/default/s5-core.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/s5-core.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/s5-core.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/slides.css b/python/helpers/py2only/docutils/writers/s5_html/themes/default/slides.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/default/slides.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/slides.css diff --git a/python/helpers/docutils/writers/s5_html/themes/default/slides.js b/python/helpers/py2only/docutils/writers/s5_html/themes/default/slides.js similarity index 99% rename from python/helpers/docutils/writers/s5_html/themes/default/slides.js rename to python/helpers/py2only/docutils/writers/s5_html/themes/default/slides.js index 81e04e5d442f..2ebbbd8e3e7a 100644 --- a/python/helpers/docutils/writers/s5_html/themes/default/slides.js +++ b/python/helpers/py2only/docutils/writers/s5_html/themes/default/slides.js @@ -8,7 +8,7 @@ var undef; var slideCSS = ''; var snum = 0; var smax = 1; -var slideIDs = new Array(); +var slideIDs = []; var incpos = 0; var number = undef; var s5mode = true; @@ -45,7 +45,7 @@ function addClass(object,className) { function GetElementsWithClassName(elementName,className) { var allElements = document.getElementsByTagName(elementName); - var elemColl = new Array(); + var elemColl = []; for (var i = 0; i< allElements.length; i++) { if (hasClass(allElements[i], className)) { elemColl[elemColl.length] = allElements[i]; @@ -465,7 +465,7 @@ function notOperaFix() { } function getIncrementals(obj) { - var incrementals = new Array(); + var incrementals = []; if (!obj) return incrementals; var children = obj.childNodes; @@ -500,7 +500,7 @@ function getIncrementals(obj) { } function createIncrementals() { - var incrementals = new Array(); + var incrementals = []; for (var i = 0; i < smax; i++) { incrementals[i] = getIncrementals(document.getElementById(slideIDs[i])); } diff --git a/python/helpers/docutils/writers/s5_html/themes/medium-black/__base__ b/python/helpers/py2only/docutils/writers/s5_html/themes/medium-black/__base__ similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/medium-black/__base__ rename to python/helpers/py2only/docutils/writers/s5_html/themes/medium-black/__base__ diff --git a/python/helpers/docutils/writers/s5_html/themes/medium-black/pretty.css b/python/helpers/py2only/docutils/writers/s5_html/themes/medium-black/pretty.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/medium-black/pretty.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/medium-black/pretty.css diff --git a/python/helpers/docutils/writers/s5_html/themes/medium-white/framing.css b/python/helpers/py2only/docutils/writers/s5_html/themes/medium-white/framing.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/medium-white/framing.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/medium-white/framing.css diff --git a/python/helpers/docutils/writers/s5_html/themes/medium-white/pretty.css b/python/helpers/py2only/docutils/writers/s5_html/themes/medium-white/pretty.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/medium-white/pretty.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/medium-white/pretty.css diff --git a/python/helpers/docutils/writers/s5_html/themes/small-black/__base__ b/python/helpers/py2only/docutils/writers/s5_html/themes/small-black/__base__ similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/small-black/__base__ rename to python/helpers/py2only/docutils/writers/s5_html/themes/small-black/__base__ diff --git a/python/helpers/docutils/writers/s5_html/themes/small-black/pretty.css b/python/helpers/py2only/docutils/writers/s5_html/themes/small-black/pretty.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/small-black/pretty.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/small-black/pretty.css diff --git a/python/helpers/docutils/writers/s5_html/themes/small-white/framing.css b/python/helpers/py2only/docutils/writers/s5_html/themes/small-white/framing.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/small-white/framing.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/small-white/framing.css diff --git a/python/helpers/docutils/writers/s5_html/themes/small-white/pretty.css b/python/helpers/py2only/docutils/writers/s5_html/themes/small-white/pretty.css similarity index 100% rename from python/helpers/docutils/writers/s5_html/themes/small-white/pretty.css rename to python/helpers/py2only/docutils/writers/s5_html/themes/small-white/pretty.css diff --git a/python/helpers/py3only/docutils/__init__.py b/python/helpers/py3only/docutils/__init__.py new file mode 100644 index 000000000000..0bcc6a4af264 --- /dev/null +++ b/python/helpers/py3only/docutils/__init__.py @@ -0,0 +1,217 @@ +# $Id: __init__.py 7756 2014-07-06 11:48:05Z grubert $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This is the Docutils (Python Documentation Utilities) package. + +Package Structure +================= + +Modules: + +- __init__.py: Contains component base classes, exception classes, and + Docutils version information. + +- core.py: Contains the ``Publisher`` class and ``publish_*()`` convenience + functions. + +- frontend.py: Runtime settings (command-line interface, configuration files) + processing, for Docutils front-ends. + +- io.py: Provides a uniform API for low-level input and output. + +- nodes.py: Docutils document tree (doctree) node class library. + +- statemachine.py: A finite state machine specialized for + regular-expression-based text filters. + +Subpackages: + +- languages: Language-specific mappings of terms. + +- parsers: Syntax-specific input parser modules or packages. + +- readers: Context-specific input handlers which understand the data + source and manage a parser. + +- transforms: Modules used by readers and writers to modify DPS + doctrees. + +- utils: Contains the ``Reporter`` system warning class and miscellaneous + utilities used by readers, writers, and transforms. + + utils/urischemes.py: Contains a complete mapping of known URI addressing + scheme names to descriptions. + +- utils/math: Contains functions for conversion of mathematical notation + between different formats (LaTeX, MathML, text, ...). + +- writers: Format-specific output translators. +""" + +__docformat__ = 'reStructuredText' + +__version__ = '0.12' +"""``major.minor.micro`` version number. The micro number is bumped for API +changes, for new functionality, and for interim project releases. The minor +number is bumped whenever there is a significant project release. The major +number will be bumped when the project is feature-complete, and perhaps if +there is a major change in the design.""" + +__version_details__ = 'release' +"""Extra version details (e.g. 'snapshot 2005-05-29, r3410', 'repository', +'release'), modified automatically & manually.""" + +import sys + +class ApplicationError(Exception): + # Workaround: + # In Python < 2.6, unicode() calls `str` on the + # arg and therefore, e.g., unicode(StandardError(u'\u234')) fails + # with UnicodeDecodeError. + if sys.version_info < (2,6): + def __unicode__(self): + return ', '.join(self.args) + +class DataError(ApplicationError): pass + + +class SettingsSpec: + + """ + Runtime setting specification base class. + + SettingsSpec subclass objects used by `docutils.frontend.OptionParser`. + """ + + settings_spec = () + """Runtime settings specification. Override in subclasses. + + Defines runtime settings and associated command-line options, as used by + `docutils.frontend.OptionParser`. This is a tuple of: + + - Option group title (string or `None` which implies no group, just a list + of single options). + + - Description (string or `None`). + + - A sequence of option tuples. Each consists of: + + - Help text (string) + + - List of option strings (e.g. ``['-Q', '--quux']``). + + - Dictionary of keyword arguments sent to the OptionParser/OptionGroup + ``add_option`` method. + + Runtime setting names are derived implicitly from long option names + ('--a-setting' becomes ``settings.a_setting``) or explicitly from the + 'dest' keyword argument. + + Most settings will also have a 'validator' keyword & function. The + validator function validates setting values (from configuration files + and command-line option arguments) and converts them to appropriate + types. For example, the ``docutils.frontend.validate_boolean`` + function, **required by all boolean settings**, converts true values + ('1', 'on', 'yes', and 'true') to 1 and false values ('0', 'off', + 'no', 'false', and '') to 0. Validators need only be set once per + setting. See the `docutils.frontend.validate_*` functions. + + See the optparse docs for more details. + + - More triples of group title, description, options, as many times as + needed. Thus, `settings_spec` tuples can be simply concatenated. + """ + + settings_defaults = None + """A dictionary of defaults for settings not in `settings_spec` (internal + settings, intended to be inaccessible by command-line and config file). + Override in subclasses.""" + + settings_default_overrides = None + """A dictionary of auxiliary defaults, to override defaults for settings + defined in other components. Override in subclasses.""" + + relative_path_settings = () + """Settings containing filesystem paths. Override in subclasses. + Settings listed here are to be interpreted relative to the current working + directory.""" + + config_section = None + """The name of the config file section specific to this component + (lowercase, no brackets). Override in subclasses.""" + + config_section_dependencies = None + """A list of names of config file sections that are to be applied before + `config_section`, in order (from general to specific). In other words, + the settings in `config_section` are to be overlaid on top of the settings + from these sections. The "general" section is assumed implicitly. + Override in subclasses.""" + + +class TransformSpec: + + """ + Runtime transform specification base class. + + TransformSpec subclass objects used by `docutils.transforms.Transformer`. + """ + + def get_transforms(self): + """Transforms required by this class. Override in subclasses.""" + if self.default_transforms != (): + import warnings + warnings.warn('default_transforms attribute deprecated.\n' + 'Use get_transforms() method instead.', + DeprecationWarning) + return list(self.default_transforms) + return [] + + # Deprecated; for compatibility. + default_transforms = () + + unknown_reference_resolvers = () + """List of functions to try to resolve unknown references. Unknown + references have a 'refname' attribute which doesn't correspond to any + target in the document. Called when the transforms in + `docutils.tranforms.references` are unable to find a correct target. The + list should contain functions which will try to resolve unknown + references, with the following signature:: + + def reference_resolver(node): + '''Returns boolean: true if resolved, false if not.''' + + If the function is able to resolve the reference, it should also remove + the 'refname' attribute and mark the node as resolved:: + + del node['refname'] + node.resolved = 1 + + Each function must have a "priority" attribute which will affect the order + the unknown_reference_resolvers are run:: + + reference_resolver.priority = 100 + + Override in subclasses.""" + + +class Component(SettingsSpec, TransformSpec): + + """Base class for Docutils components.""" + + component_type = None + """Name of the component type ('reader', 'parser', 'writer'). Override in + subclasses.""" + + supported = () + """Names for this component. Override in subclasses.""" + + def supports(self, format): + """ + Is `format` supported by this component? + + To be used by transforms to ask the dependent component if it supports + a certain input context or output format. + """ + return format in self.supported diff --git a/python/helpers/py3only/docutils/_compat.py b/python/helpers/py3only/docutils/_compat.py new file mode 100644 index 000000000000..3c942816debf --- /dev/null +++ b/python/helpers/py3only/docutils/_compat.py @@ -0,0 +1,47 @@ +# $Id: _compat.py 7486 2012-07-11 12:25:14Z milde $ +# Author: Georg Brandl +# Copyright: This module has been placed in the public domain. + +""" +Python 2/3 compatibility definitions. + +This module currently provides the following helper symbols: + +* bytes (name of byte string type; str in 2.x, bytes in 3.x) +* b (function converting a string literal to an ASCII byte string; + can be also used to convert a Unicode string into a byte string) +* u_prefix (unicode repr prefix: 'u' in 2.x, '' in 3.x) + (Required in docutils/test/test_publisher.py) +* BytesIO (a StringIO class that works with bytestrings) +""" + +import sys + +if sys.version_info < (3,0): + b = bytes = str + u_prefix = 'u' +else: + import builtins + bytes = builtins.bytes + u_prefix = '' + def b(s): + if isinstance(s, str): + return s.encode('latin1') + elif isinstance(s, bytes): + return s + else: + raise TypeError("Invalid argument %r for b()" % (s,)) + # using this hack since 2to3 "fixes" the relative import + # when using ``from io import BytesIO`` + BytesIO = __import__('io').BytesIO + +if sys.version_info < (2,5): + import builtins + + def __import__(name, globals={}, locals={}, fromlist=[], level=-1): + """Compatibility definition for Python 2.4. + + Silently ignore the `level` argument missing in Python < 2.5. + """ + # we need the level arg because the default changed in Python 3.3 + return builtins.__import__(name, globals, locals, fromlist) diff --git a/python/helpers/py3only/docutils/core.py b/python/helpers/py3only/docutils/core.py new file mode 100644 index 000000000000..ac26350c12fa --- /dev/null +++ b/python/helpers/py3only/docutils/core.py @@ -0,0 +1,664 @@ +# $Id: core.py 7466 2012-06-25 14:56:51Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Calling the ``publish_*`` convenience functions (or instantiating a +`Publisher` object) with component names will result in default +behavior. For custom behavior (setting component options), create +custom component objects first, and pass *them* to +``publish_*``/`Publisher`. See `The Docutils Publisher`_. + +.. _The Docutils Publisher: http://docutils.sf.net/docs/api/publisher.html +""" + +__docformat__ = 'reStructuredText' + +import pprint +import sys + +import docutils.readers.doctree +from docutils import __version__, __version_details__, SettingsSpec +from docutils import frontend, io, utils, readers, writers +from docutils.frontend import OptionParser +from docutils.utils.error_reporting import ErrorOutput, ErrorString + + +class Publisher: + + """ + A facade encapsulating the high-level logic of a Docutils system. + """ + + def __init__(self, reader=None, parser=None, writer=None, + source=None, source_class=io.FileInput, + destination=None, destination_class=io.FileOutput, + settings=None): + """ + Initial setup. If any of `reader`, `parser`, or `writer` are not + specified, the corresponding ``set_...`` method should be called with + a component name (`set_reader` sets the parser as well). + """ + + self.document = None + """The document tree (`docutils.nodes` objects).""" + + self.reader = reader + """A `docutils.readers.Reader` instance.""" + + self.parser = parser + """A `docutils.parsers.Parser` instance.""" + + self.writer = writer + """A `docutils.writers.Writer` instance.""" + + for component in 'reader', 'parser', 'writer': + assert not isinstance(getattr(self, component), str), ( + 'passed string "%s" as "%s" parameter; pass an instance, ' + 'or use the "%s_name" parameter instead (in ' + 'docutils.core.publish_* convenience functions).' + % (getattr(self, component), component, component)) + + self.source = source + """The source of input data, a `docutils.io.Input` instance.""" + + self.source_class = source_class + """The class for dynamically created source objects.""" + + self.destination = destination + """The destination for docutils output, a `docutils.io.Output` + instance.""" + + self.destination_class = destination_class + """The class for dynamically created destination objects.""" + + self.settings = settings + """An object containing Docutils settings as instance attributes. + Set by `self.process_command_line()` or `self.get_settings()`.""" + + self._stderr = ErrorOutput() + + def set_reader(self, reader_name, parser, parser_name): + """Set `self.reader` by name.""" + reader_class = readers.get_reader_class(reader_name) + self.reader = reader_class(parser, parser_name) + self.parser = self.reader.parser + + def set_writer(self, writer_name): + """Set `self.writer` by name.""" + writer_class = writers.get_writer_class(writer_name) + self.writer = writer_class() + + def set_components(self, reader_name, parser_name, writer_name): + if self.reader is None: + self.set_reader(reader_name, self.parser, parser_name) + if self.parser is None: + if self.reader.parser is None: + self.reader.set_parser(parser_name) + self.parser = self.reader.parser + if self.writer is None: + self.set_writer(writer_name) + + def setup_option_parser(self, usage=None, description=None, + settings_spec=None, config_section=None, + **defaults): + if config_section: + if not settings_spec: + settings_spec = SettingsSpec() + settings_spec.config_section = config_section + parts = config_section.split() + if len(parts) > 1 and parts[-1] == 'application': + settings_spec.config_section_dependencies = ['applications'] + #@@@ Add self.source & self.destination to components in future? + option_parser = OptionParser( + components=(self.parser, self.reader, self.writer, settings_spec), + defaults=defaults, read_config_files=True, + usage=usage, description=description) + return option_parser + + def get_settings(self, usage=None, description=None, + settings_spec=None, config_section=None, **defaults): + """ + Set and return default settings (overrides in `defaults` dict). + + Set components first (`self.set_reader` & `self.set_writer`). + Explicitly setting `self.settings` disables command line option + processing from `self.publish()`. + """ + option_parser = self.setup_option_parser( + usage, description, settings_spec, config_section, **defaults) + self.settings = option_parser.get_default_values() + return self.settings + + def process_programmatic_settings(self, settings_spec, + settings_overrides, + config_section): + if self.settings is None: + defaults = (settings_overrides or {}).copy() + # Propagate exceptions by default when used programmatically: + defaults.setdefault('traceback', True) + self.get_settings(settings_spec=settings_spec, + config_section=config_section, + **defaults) + + def process_command_line(self, argv=None, usage=None, description=None, + settings_spec=None, config_section=None, + **defaults): + """ + Pass an empty list to `argv` to avoid reading `sys.argv` (the + default). + + Set components first (`self.set_reader` & `self.set_writer`). + """ + option_parser = self.setup_option_parser( + usage, description, settings_spec, config_section, **defaults) + if argv is None: + argv = sys.argv[1:] + # converting to Unicode (Python 3 does this automatically): + if sys.version_info < (3,0): + # TODO: make this failsafe and reversible? + argv_encoding = (frontend.locale_encoding or 'ascii') + argv = [a.decode(argv_encoding) for a in argv] + self.settings = option_parser.parse_args(argv) + + def set_io(self, source_path=None, destination_path=None): + if self.source is None: + self.set_source(source_path=source_path) + if self.destination is None: + self.set_destination(destination_path=destination_path) + + def set_source(self, source=None, source_path=None): + if source_path is None: + source_path = self.settings._source + else: + self.settings._source = source_path + # Raise IOError instead of system exit with `tracback == True` + # TODO: change io.FileInput's default behaviour and remove this hack + try: + self.source = self.source_class( + source=source, source_path=source_path, + encoding=self.settings.input_encoding) + except TypeError: + self.source = self.source_class( + source=source, source_path=source_path, + encoding=self.settings.input_encoding) + + def set_destination(self, destination=None, destination_path=None): + if destination_path is None: + destination_path = self.settings._destination + else: + self.settings._destination = destination_path + self.destination = self.destination_class( + destination=destination, destination_path=destination_path, + encoding=self.settings.output_encoding, + error_handler=self.settings.output_encoding_error_handler) + + def apply_transforms(self): + self.document.transformer.populate_from_components( + (self.source, self.reader, self.reader.parser, self.writer, + self.destination)) + self.document.transformer.apply_transforms() + + def publish(self, argv=None, usage=None, description=None, + settings_spec=None, settings_overrides=None, + config_section=None, enable_exit_status=False): + """ + Process command line options and arguments (if `self.settings` not + already set), run `self.reader` and then `self.writer`. Return + `self.writer`'s output. + """ + exit = None + try: + if self.settings is None: + self.process_command_line( + argv, usage, description, settings_spec, config_section, + **(settings_overrides or {})) + self.set_io() + self.document = self.reader.read(self.source, self.parser, + self.settings) + self.apply_transforms() + output = self.writer.write(self.document, self.destination) + self.writer.assemble_parts() + except SystemExit as error: + exit = 1 + exit_status = error.code + except Exception as error: + if not self.settings: # exception too early to report nicely + raise + if self.settings.traceback: # Propagate exceptions? + self.debugging_dumps() + raise + self.report_Exception(error) + exit = True + exit_status = 1 + self.debugging_dumps() + if (enable_exit_status and self.document + and (self.document.reporter.max_level + >= self.settings.exit_status_level)): + sys.exit(self.document.reporter.max_level + 10) + elif exit: + sys.exit(exit_status) + return output + + def debugging_dumps(self): + if not self.document: + return + if self.settings.dump_settings: + print('\n::: Runtime settings:', file=self._stderr) + print(pprint.pformat(self.settings.__dict__), file=self._stderr) + if self.settings.dump_internals: + print('\n::: Document internals:', file=self._stderr) + print(pprint.pformat(self.document.__dict__), file=self._stderr) + if self.settings.dump_transforms: + print('\n::: Transforms applied:', file=self._stderr) + print((' (priority, transform class, ' + 'pending node details, keyword args)'), file=self._stderr) + print(pprint.pformat( + [(priority, '%s.%s' % (xclass.__module__, xclass.__name__), + pending and pending.details, kwargs) + for priority, xclass, pending, kwargs + in self.document.transformer.applied]), file=self._stderr) + if self.settings.dump_pseudo_xml: + print('\n::: Pseudo-XML:', file=self._stderr) + print(self.document.pformat().encode( + 'raw_unicode_escape'), file=self._stderr) + + def report_Exception(self, error): + if isinstance(error, utils.SystemMessage): + self.report_SystemMessage(error) + elif isinstance(error, UnicodeEncodeError): + self.report_UnicodeError(error) + elif isinstance(error, io.InputError): + self._stderr.write('Unable to open source file for reading:\n' + ' %s\n' % ErrorString(error)) + elif isinstance(error, io.OutputError): + self._stderr.write( + 'Unable to open destination file for writing:\n' + ' %s\n' % ErrorString(error)) + else: + print('%s' % ErrorString(error), file=self._stderr) + print(("""\ +Exiting due to error. Use "--traceback" to diagnose. +Please report errors to . +Include "--traceback" output, Docutils version (%s [%s]), +Python version (%s), your OS type & version, and the +command line used.""" % (__version__, __version_details__, + sys.version.split()[0])), file=self._stderr) + + def report_SystemMessage(self, error): + print(('Exiting due to level-%s (%s) system message.' + % (error.level, + utils.Reporter.levels[error.level])), file=self._stderr) + + def report_UnicodeError(self, error): + data = error.object[error.start:error.end] + self._stderr.write( + '%s\n' + '\n' + 'The specified output encoding (%s) cannot\n' + 'handle all of the output.\n' + 'Try setting "--output-encoding-error-handler" to\n' + '\n' + '* "xmlcharrefreplace" (for HTML & XML output);\n' + ' the output will contain "%s" and should be usable.\n' + '* "backslashreplace" (for other output formats);\n' + ' look for "%s" in the output.\n' + '* "replace"; look for "?" in the output.\n' + '\n' + '"--output-encoding-error-handler" is currently set to "%s".\n' + '\n' + 'Exiting due to error. Use "--traceback" to diagnose.\n' + 'If the advice above doesn\'t eliminate the error,\n' + 'please report it to .\n' + 'Include "--traceback" output, Docutils version (%s),\n' + 'Python version (%s), your OS type & version, and the\n' + 'command line used.\n' + % (ErrorString(error), + self.settings.output_encoding, + data.encode('ascii', 'xmlcharrefreplace'), + data.encode('ascii', 'backslashreplace'), + self.settings.output_encoding_error_handler, + __version__, sys.version.split()[0])) + +default_usage = '%prog [options] [ []]' +default_description = ('Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +def publish_cmdline(reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=True, argv=None, + usage=default_usage, description=default_description): + """ + Set up & run a `Publisher` for command-line-based file I/O (input and + output file paths taken automatically from the command line). Return the + encoded string output also. + + Parameters: see `publish_programmatically` for the remainder. + + - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``. + - `usage`: Usage string, output if there's a problem parsing the command + line. + - `description`: Program description, output for the "--help" option + (along with command-line option descriptions). + """ + pub = Publisher(reader, parser, writer, settings=settings) + pub.set_components(reader_name, parser_name, writer_name) + output = pub.publish( + argv, usage, description, settings_spec, settings_overrides, + config_section=config_section, enable_exit_status=enable_exit_status) + return output + +def publish_file(source=None, source_path=None, + destination=None, destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, settings_overrides=None, + config_section=None, enable_exit_status=False): + """ + Set up & run a `Publisher` for programmatic use with file-like I/O. + Return the encoded string output also. + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source_class=io.FileInput, source=source, source_path=source_path, + destination_class=io.FileOutput, + destination=destination, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return output + +def publish_string(source, source_path=None, destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=False): + """ + Set up & run a `Publisher` for programmatic use with string I/O. Return + the encoded string or Unicode string output. + + For encoded string output, be sure to set the 'output_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + output. Here's one way:: + + publish_string(..., settings_overrides={'output_encoding': 'unicode'}) + + Similarly for Unicode string input (`source`):: + + publish_string(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source_class=io.StringInput, source=source, source_path=source_path, + destination_class=io.StringOutput, + destination=None, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return output + +def publish_parts(source, source_path=None, source_class=io.StringInput, + destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=False): + """ + Set up & run a `Publisher`, and return a dictionary of document parts. + Dictionary keys are the names of parts, and values are Unicode strings; + encoding is up to the client. For programmatic use with string I/O. + + For encoded string input, be sure to set the 'input_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + input. Here's how:: + + publish_parts(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source=source, source_path=source_path, source_class=source_class, + destination_class=io.StringOutput, + destination=None, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return pub.writer.parts + +def publish_doctree(source, source_path=None, + source_class=io.StringInput, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=False): + """ + Set up & run a `Publisher` for programmatic use with string I/O. + Return the document tree. + + For encoded string input, be sure to set the 'input_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + input. Here's one way:: + + publish_doctree(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + pub = Publisher(reader=reader, parser=parser, writer=None, + settings=settings, + source_class=source_class, + destination_class=io.NullOutput) + pub.set_components(reader_name, parser_name, 'null') + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_source(source, source_path) + pub.set_destination(None, None) + output = pub.publish(enable_exit_status=enable_exit_status) + return pub.document + +def publish_from_doctree(document, destination_path=None, + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=False): + """ + Set up & run a `Publisher` to render from an existing document + tree data structure, for programmatic use with string I/O. Return + the encoded string output. + + Note that document.settings is overridden; if you want to use the settings + of the original `document`, pass settings=document.settings. + + Also, new document.transformer and document.reporter objects are + generated. + + For encoded string output, be sure to set the 'output_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + output. Here's one way:: + + publish_from_doctree( + ..., settings_overrides={'output_encoding': 'unicode'}) + + Parameters: `document` is a `docutils.nodes.document` object, an existing + document tree. + + Other parameters: see `publish_programmatically`. + """ + reader = docutils.readers.doctree.Reader(parser_name='null') + pub = Publisher(reader, None, writer, + source=io.DocTreeInput(document), + destination_class=io.StringOutput, settings=settings) + if not writer and writer_name: + pub.set_writer(writer_name) + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_destination(None, destination_path) + return pub.publish(enable_exit_status=enable_exit_status) + +def publish_cmdline_to_binary(reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=True, argv=None, + usage=default_usage, description=default_description, + destination=None, destination_class=io.BinaryFileOutput + ): + """ + Set up & run a `Publisher` for command-line-based file I/O (input and + output file paths taken automatically from the command line). Return the + encoded string output also. + + This is just like publish_cmdline, except that it uses + io.BinaryFileOutput instead of io.FileOutput. + + Parameters: see `publish_programmatically` for the remainder. + + - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``. + - `usage`: Usage string, output if there's a problem parsing the command + line. + - `description`: Program description, output for the "--help" option + (along with command-line option descriptions). + """ + pub = Publisher(reader, parser, writer, settings=settings, + destination_class=destination_class) + pub.set_components(reader_name, parser_name, writer_name) + output = pub.publish( + argv, usage, description, settings_spec, settings_overrides, + config_section=config_section, enable_exit_status=enable_exit_status) + return output + +def publish_programmatically(source_class, source, source_path, + destination_class, destination, destination_path, + reader, reader_name, + parser, parser_name, + writer, writer_name, + settings, settings_spec, + settings_overrides, config_section, + enable_exit_status): + """ + Set up & run a `Publisher` for custom programmatic use. Return the + encoded string output and the Publisher object. + + Applications should not need to call this function directly. If it does + seem to be necessary to call this function directly, please write to the + Docutils-develop mailing list + . + + Parameters: + + * `source_class` **required**: The class for dynamically created source + objects. Typically `io.FileInput` or `io.StringInput`. + + * `source`: Type depends on `source_class`: + + - If `source_class` is `io.FileInput`: Either a file-like object + (must have 'read' and 'close' methods), or ``None`` + (`source_path` is opened). If neither `source` nor + `source_path` are supplied, `sys.stdin` is used. + + - If `source_class` is `io.StringInput` **required**: The input + string, either an encoded 8-bit string (set the + 'input_encoding' setting to the correct encoding) or a Unicode + string (set the 'input_encoding' setting to 'unicode'). + + * `source_path`: Type depends on `source_class`: + + - `io.FileInput`: Path to the input file, opened if no `source` + supplied. + + - `io.StringInput`: Optional. Path to the file or object that produced + `source`. Only used for diagnostic output. + + * `destination_class` **required**: The class for dynamically created + destination objects. Typically `io.FileOutput` or `io.StringOutput`. + + * `destination`: Type depends on `destination_class`: + + - `io.FileOutput`: Either a file-like object (must have 'write' and + 'close' methods), or ``None`` (`destination_path` is opened). If + neither `destination` nor `destination_path` are supplied, + `sys.stdout` is used. + + - `io.StringOutput`: Not used; pass ``None``. + + * `destination_path`: Type depends on `destination_class`: + + - `io.FileOutput`: Path to the output file. Opened if no `destination` + supplied. + + - `io.StringOutput`: Path to the file or object which will receive the + output; optional. Used for determining relative paths (stylesheets, + source links, etc.). + + * `reader`: A `docutils.readers.Reader` object. + + * `reader_name`: Name or alias of the Reader class to be instantiated if + no `reader` supplied. + + * `parser`: A `docutils.parsers.Parser` object. + + * `parser_name`: Name or alias of the Parser class to be instantiated if + no `parser` supplied. + + * `writer`: A `docutils.writers.Writer` object. + + * `writer_name`: Name or alias of the Writer class to be instantiated if + no `writer` supplied. + + * `settings`: A runtime settings (`docutils.frontend.Values`) object, for + dotted-attribute access to runtime settings. It's the end result of the + `SettingsSpec`, config file, and option processing. If `settings` is + passed, it's assumed to be complete and no further setting/config/option + processing is done. + + * `settings_spec`: A `docutils.SettingsSpec` subclass or object. Provides + extra application-specific settings definitions independently of + components. In other words, the application becomes a component, and + its settings data is processed along with that of the other components. + Used only if no `settings` specified. + + * `settings_overrides`: A dictionary containing application-specific + settings defaults that override the defaults of other components. + Used only if no `settings` specified. + + * `config_section`: A string, the name of the configuration file section + for this application. Overrides the ``config_section`` attribute + defined by `settings_spec`. Used only if no `settings` specified. + + * `enable_exit_status`: Boolean; enable exit status at end of processing? + """ + pub = Publisher(reader, parser, writer, settings=settings, + source_class=source_class, + destination_class=destination_class) + pub.set_components(reader_name, parser_name, writer_name) + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_source(source, source_path) + pub.set_destination(destination, destination_path) + output = pub.publish(enable_exit_status=enable_exit_status) + return output, pub diff --git a/python/helpers/py3only/docutils/examples.py b/python/helpers/py3only/docutils/examples.py new file mode 100644 index 000000000000..395dbbf14fc2 --- /dev/null +++ b/python/helpers/py3only/docutils/examples.py @@ -0,0 +1,97 @@ +# $Id: examples.py 7320 2012-01-19 22:33:02Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This module contains practical examples of Docutils client code. + +Importing this module from client code is not recommended; its contents are +subject to change in future Docutils releases. Instead, it is recommended +that you copy and paste the parts you need into your own code, modifying as +necessary. +""" + +from docutils import core, io + + +def html_parts(input_string, source_path=None, destination_path=None, + input_encoding='unicode', doctitle=True, + initial_header_level=1): + """ + Given an input string, returns a dictionary of HTML document parts. + + Dictionary keys are the names of parts, and values are Unicode strings; + encoding is up to the client. + + Parameters: + + - `input_string`: A multi-line text string; required. + - `source_path`: Path to the source file or object. Optional, but useful + for diagnostic output (system messages). + - `destination_path`: Path to the file or object which will receive the + output; optional. Used for determining relative paths (stylesheets, + source links, etc.). + - `input_encoding`: The encoding of `input_string`. If it is an encoded + 8-bit string, provide the correct encoding. If it is a Unicode string, + use "unicode", the default. + - `doctitle`: Disable the promotion of a lone top-level section title to + document title (and subsequent section title to document subtitle + promotion); enabled by default. + - `initial_header_level`: The initial level for header elements (e.g. 1 + for "

"). + """ + overrides = {'input_encoding': input_encoding, + 'doctitle_xform': doctitle, + 'initial_header_level': initial_header_level} + parts = core.publish_parts( + source=input_string, source_path=source_path, + destination_path=destination_path, + writer_name='html', settings_overrides=overrides) + return parts + +def html_body(input_string, source_path=None, destination_path=None, + input_encoding='unicode', output_encoding='unicode', + doctitle=True, initial_header_level=1): + """ + Given an input string, returns an HTML fragment as a string. + + The return value is the contents of the element. + + Parameters (see `html_parts()` for the remainder): + + - `output_encoding`: The desired encoding of the output. If a Unicode + string is desired, use the default value of "unicode" . + """ + parts = html_parts( + input_string=input_string, source_path=source_path, + destination_path=destination_path, + input_encoding=input_encoding, doctitle=doctitle, + initial_header_level=initial_header_level) + fragment = parts['html_body'] + if output_encoding != 'unicode': + fragment = fragment.encode(output_encoding) + return fragment + +def internals(input_string, source_path=None, destination_path=None, + input_encoding='unicode', settings_overrides=None): + """ + Return the document tree and publisher, for exploring Docutils internals. + + Parameters: see `html_parts()`. + """ + if settings_overrides: + overrides = settings_overrides.copy() + else: + overrides = {} + overrides['input_encoding'] = input_encoding + output, pub = core.publish_programmatically( + source_class=io.StringInput, source=input_string, + source_path=source_path, + destination_class=io.NullOutput, destination=None, + destination_path=destination_path, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='null', + settings=None, settings_spec=None, settings_overrides=overrides, + config_section=None, enable_exit_status=None) + return pub.writer.document, pub diff --git a/python/helpers/py3only/docutils/frontend.py b/python/helpers/py3only/docutils/frontend.py new file mode 100644 index 000000000000..0fdfa639a382 --- /dev/null +++ b/python/helpers/py3only/docutils/frontend.py @@ -0,0 +1,818 @@ +# $Id: frontend.py 7584 2013-01-01 20:00:21Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Command-line and common processing for Docutils front-end tools. + +Exports the following classes: + +* `OptionParser`: Standard Docutils command-line processing. +* `Option`: Customized version of `optparse.Option`; validation support. +* `Values`: Runtime settings; objects are simple structs + (``object.attribute``). Supports cumulative list settings (attributes). +* `ConfigParser`: Standard Docutils config file processing. + +Also exports the following functions: + +* Option callbacks: `store_multiple`, `read_config_file`. +* Setting validators: `validate_encoding`, + `validate_encoding_error_handler`, + `validate_encoding_and_error_handler`, + `validate_boolean`, `validate_ternary`, `validate_threshold`, + `validate_colon_separated_string_list`, + `validate_comma_separated_string_list`, + `validate_dependency_file`. +* `make_paths_absolute`. +* SettingSpec manipulation: `filter_settings_spec`. +""" + +__docformat__ = 'reStructuredText' + +import codecs +import optparse +import os +import os.path +import sys +import warnings +from optparse import SUPPRESS_HELP + +import configparser as CP + +import docutils +import docutils.nodes +import docutils.utils +from docutils.utils.error_reporting import locale_encoding, ErrorOutput, ErrorString + + +def store_multiple(option, opt, value, parser, *args, **kwargs): + """ + Store multiple values in `parser.values`. (Option callback.) + + Store `None` for each attribute named in `args`, and store the value for + each key (attribute name) in `kwargs`. + """ + for attribute in args: + setattr(parser.values, attribute, None) + for key, value in list(kwargs.items()): + setattr(parser.values, key, value) + +def read_config_file(option, opt, value, parser): + """ + Read a configuration file during option processing. (Option callback.) + """ + try: + new_settings = parser.get_config_file_settings(value) + except ValueError as error: + parser.error(error) + parser.values.update(new_settings, parser) + +def validate_encoding(setting, value, option_parser, + config_parser=None, config_section=None): + try: + codecs.lookup(value) + except LookupError: + raise LookupError('setting "%s": unknown encoding: "%s"' + % (setting, value)) + return value + +def validate_encoding_error_handler(setting, value, option_parser, + config_parser=None, config_section=None): + try: + codecs.lookup_error(value) + except LookupError: + raise LookupError( + 'unknown encoding error handler: "%s" (choices: ' + '"strict", "ignore", "replace", "backslashreplace", ' + '"xmlcharrefreplace", and possibly others; see documentation for ' + 'the Python ``codecs`` module)' % value) + return value + +def validate_encoding_and_error_handler( + setting, value, option_parser, config_parser=None, config_section=None): + """ + Side-effect: if an error handler is included in the value, it is inserted + into the appropriate place as if it was a separate setting/option. + """ + if ':' in value: + encoding, handler = value.split(':') + validate_encoding_error_handler( + setting + '_error_handler', handler, option_parser, + config_parser, config_section) + if config_parser: + config_parser.set(config_section, setting + '_error_handler', + handler) + else: + setattr(option_parser.values, setting + '_error_handler', handler) + else: + encoding = value + validate_encoding(setting, encoding, option_parser, + config_parser, config_section) + return encoding + +def validate_boolean(setting, value, option_parser, + config_parser=None, config_section=None): + """Check/normalize boolean settings: + True: '1', 'on', 'yes', 'true' + False: '0', 'off', 'no','false', '' + """ + if isinstance(value, bool): + return value + try: + return option_parser.booleans[value.strip().lower()] + except KeyError: + raise LookupError('unknown boolean value: "%s"' % value) + +def validate_ternary(setting, value, option_parser, + config_parser=None, config_section=None): + """Check/normalize three-value settings: + True: '1', 'on', 'yes', 'true' + False: '0', 'off', 'no','false', '' + any other value: returned as-is. + """ + if isinstance(value, bool) or value is None: + return value + try: + return option_parser.booleans[value.strip().lower()] + except KeyError: + return value + +def validate_nonnegative_int(setting, value, option_parser, + config_parser=None, config_section=None): + value = int(value) + if value < 0: + raise ValueError('negative value; must be positive or zero') + return value + +def validate_threshold(setting, value, option_parser, + config_parser=None, config_section=None): + try: + return int(value) + except ValueError: + try: + return option_parser.thresholds[value.lower()] + except (KeyError, AttributeError): + raise LookupError('unknown threshold: %r.' % value) + +def validate_colon_separated_string_list( + setting, value, option_parser, config_parser=None, config_section=None): + if not isinstance(value, list): + value = value.split(':') + else: + last = value.pop() + value.extend(last.split(':')) + return value + +def validate_comma_separated_list(setting, value, option_parser, + config_parser=None, config_section=None): + """Check/normalize list arguments (split at "," and strip whitespace). + """ + # `value` is already a ``list`` when given as command line option + # and "action" is "append" and ``unicode`` or ``str`` else. + if not isinstance(value, list): + value = [value] + # this function is called for every option added to `value` + # -> split the last item and append the result: + last = value.pop() + items = [i.strip(' \t\n') for i in last.split(',') if i.strip(' \t\n')] + value.extend(items) + return value + +def validate_url_trailing_slash( + setting, value, option_parser, config_parser=None, config_section=None): + if not value: + return './' + elif value.endswith('/'): + return value + else: + return value + '/' + +def validate_dependency_file(setting, value, option_parser, + config_parser=None, config_section=None): + try: + return docutils.utils.DependencyList(value) + except IOError: + return docutils.utils.DependencyList(None) + +def validate_strip_class(setting, value, option_parser, + config_parser=None, config_section=None): + # value is a comma separated string list: + value = validate_comma_separated_list(setting, value, option_parser, + config_parser, config_section) + # validate list elements: + for cls in value: + normalized = docutils.nodes.make_id(cls) + if cls != normalized: + raise ValueError('invalid class value %r (perhaps %r?)' + % (cls, normalized)) + return value + +def make_paths_absolute(pathdict, keys, base_path=None): + """ + Interpret filesystem path settings relative to the `base_path` given. + + Paths are values in `pathdict` whose keys are in `keys`. Get `keys` from + `OptionParser.relative_path_settings`. + """ + if base_path is None: + base_path = os.getcwd() # type(base_path) == unicode + # to allow combining non-ASCII cwd with unicode values in `pathdict` + for key in keys: + if key in pathdict: + value = pathdict[key] + if isinstance(value, list): + value = [make_one_path_absolute(base_path, path) + for path in value] + elif value: + value = make_one_path_absolute(base_path, value) + pathdict[key] = value + +def make_one_path_absolute(base_path, path): + return os.path.abspath(os.path.join(base_path, path)) + +def filter_settings_spec(settings_spec, *exclude, **replace): + """Return a copy of `settings_spec` excluding/replacing some settings. + + `settings_spec` is a tuple of configuration settings with a structure + described for docutils.SettingsSpec.settings_spec. + + Optional positional arguments are names of to-be-excluded settings. + Keyword arguments are option specification replacements. + (See the html4strict writer for an example.) + """ + settings = list(settings_spec) + # every third item is a sequence of option tuples + for i in range(2, len(settings), 3): + newopts = [] + for opt_spec in settings[i]: + # opt_spec is ("", [

\n') + self.in_document_title = len(self.body) + elif isinstance(node.parent, nodes.section): + tag = 'h%s' % (self.section_level + self.initial_header_level - 1) + self.body.append( + self.starttag(node, tag, '', CLASS='section-subtitle') + + self.starttag({}, 'span', '', CLASS='section-subtitle')) + self.context.append('\n' % tag) + + def depart_subtitle(self, node): + self.body.append(self.context.pop()) + if self.in_document_title: + self.subtitle = self.body[self.in_document_title:-1] + self.in_document_title = 0 + self.body_pre_docinfo.extend(self.body) + self.html_subtitle.extend(self.body) + del self.body[:] + + def visit_superscript(self, node): + self.body.append(self.starttag(node, 'sup', '')) + + def depart_superscript(self, node): + self.body.append('') + + def visit_system_message(self, node): + self.body.append(self.starttag(node, 'div', CLASS='system-message')) + self.body.append('

') + backref_text = '' + if len(node['backrefs']): + backrefs = node['backrefs'] + if len(backrefs) == 1: + backref_text = ('; backlink' + % backrefs[0]) + else: + i = 1 + backlinks = [] + for backref in backrefs: + backlinks.append('%s' % (backref, i)) + i += 1 + backref_text = ('; backlinks: %s' + % ', '.join(backlinks)) + if node.hasattr('line'): + line = ', line %s' % node['line'] + else: + line = '' + self.body.append('System Message: %s/%s ' + '(%s%s)%s

\n' + % (node['type'], node['level'], + self.encode(node['source']), line, backref_text)) + + def depart_system_message(self, node): + self.body.append('\n') + + def visit_table(self, node): + self.context.append(self.compact_p) + self.compact_p = True + classes = ' '.join(['docutils', self.settings.table_style]).strip() + self.body.append( + self.starttag(node, 'table', CLASS=classes, border="1")) + + def depart_table(self, node): + self.compact_p = self.context.pop() + self.body.append('\n') + + def visit_target(self, node): + if not ('refuri' in node or 'refid' in node + or 'refname' in node): + self.body.append(self.starttag(node, 'span', '', CLASS='target')) + self.context.append('') + else: + self.context.append('') + + def depart_target(self, node): + self.body.append(self.context.pop()) + + def visit_tbody(self, node): + self.write_colspecs() + self.body.append(self.context.pop()) # '\n' or '' + self.body.append(self.starttag(node, 'tbody', valign='top')) + + def depart_tbody(self, node): + self.body.append('\n') + + def visit_term(self, node): + self.body.append(self.starttag(node, 'dt', '')) + + def depart_term(self, node): + """ + Leave the end tag to `self.visit_definition()`, in case there's a + classifier. + """ + pass + + def visit_tgroup(self, node): + # Mozilla needs : + self.body.append(self.starttag(node, 'colgroup')) + # Appended by thead or tbody: + self.context.append('\n') + node.stubs = [] + + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + self.write_colspecs() + self.body.append(self.context.pop()) # '\n' + # There may or may not be a ; this is for to use: + self.context.append('') + self.body.append(self.starttag(node, 'thead', valign='bottom')) + + def depart_thead(self, node): + self.body.append('\n') + + def visit_title(self, node): + """Only 6 section levels are supported by HTML.""" + check_id = 0 # TODO: is this a bool (False) or a counter? + close_tag = '

\n' + if isinstance(node.parent, nodes.topic): + self.body.append( + self.starttag(node, 'p', '', CLASS='topic-title first')) + elif isinstance(node.parent, nodes.sidebar): + self.body.append( + self.starttag(node, 'p', '', CLASS='sidebar-title')) + elif isinstance(node.parent, nodes.Admonition): + self.body.append( + self.starttag(node, 'p', '', CLASS='admonition-title')) + elif isinstance(node.parent, nodes.table): + self.body.append( + self.starttag(node, 'caption', '')) + close_tag = '\n' + elif isinstance(node.parent, nodes.document): + self.body.append(self.starttag(node, 'h1', '', CLASS='title')) + close_tag = '\n' + self.in_document_title = len(self.body) + else: + assert isinstance(node.parent, nodes.section) + h_level = self.section_level + self.initial_header_level - 1 + atts = {} + if (len(node.parent) >= 2 and + isinstance(node.parent[1], nodes.subtitle)): + atts['CLASS'] = 'with-subtitle' + self.body.append( + self.starttag(node, 'h%s' % h_level, '', **atts)) + atts = {} + if node.hasattr('refid'): + atts['class'] = 'toc-backref' + atts['href'] = '#' + node['refid'] + if atts: + self.body.append(self.starttag({}, 'a', '', **atts)) + close_tag = '\n' % (h_level) + else: + close_tag = '\n' % (h_level) + self.context.append(close_tag) + + def depart_title(self, node): + self.body.append(self.context.pop()) + if self.in_document_title: + self.title = self.body[self.in_document_title:-1] + self.in_document_title = 0 + self.body_pre_docinfo.extend(self.body) + self.html_title.extend(self.body) + del self.body[:] + + def visit_title_reference(self, node): + self.body.append(self.starttag(node, 'cite', '')) + + def depart_title_reference(self, node): + self.body.append('') + + def visit_topic(self, node): + self.body.append(self.starttag(node, 'div', CLASS='topic')) + self.topic_classes = node['classes'] + + def depart_topic(self, node): + self.body.append('\n') + self.topic_classes = [] + + def visit_transition(self, node): + self.body.append(self.emptytag(node, 'hr', CLASS='docutils')) + + def depart_transition(self, node): + pass + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version', meta=False) + + def depart_version(self, node): + self.depart_docinfo_item() + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' + % node.__class__.__name__) + + +class SimpleListChecker(nodes.GenericNodeVisitor): + + """ + Raise `nodes.NodeFound` if non-simple list item is encountered. + + Here "simple" means a list item containing nothing other than a single + paragraph, a simple list, or a paragraph followed by a simple list. + """ + + def default_visit(self, node): + raise nodes.NodeFound + + def visit_bullet_list(self, node): + pass + + def visit_enumerated_list(self, node): + pass + + def visit_list_item(self, node): + children = [] + for child in node.children: + if not isinstance(child, nodes.Invisible): + children.append(child) + if (children and isinstance(children[0], nodes.paragraph) + and (isinstance(children[-1], nodes.bullet_list) + or isinstance(children[-1], nodes.enumerated_list))): + children.pop() + if len(children) <= 1: + return + else: + raise nodes.NodeFound + + def visit_paragraph(self, node): + raise nodes.SkipNode + + def invisible_visit(self, node): + """Invisible nodes should be ignored.""" + raise nodes.SkipNode + + visit_comment = invisible_visit + visit_substitution_definition = invisible_visit + visit_target = invisible_visit + visit_pending = invisible_visit diff --git a/python/helpers/py3only/docutils/writers/html4css1/html4css1.css b/python/helpers/py3only/docutils/writers/html4css1/html4css1.css new file mode 100644 index 000000000000..39a036978d9d --- /dev/null +++ b/python/helpers/py3only/docutils/writers/html4css1/html4css1.css @@ -0,0 +1,327 @@ +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 7614 2013-02-21 15:55:51Z milde $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; +} + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title, .code .error { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left, object.align-left { + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right, object.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em ; + margin-right: 2em } + +pre.code .ln { color: grey; } /* line numbers */ +pre.code, code { background-color: #eeeeee } +pre.code .comment, code .comment { color: #5C6576 } +pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } +pre.code .literal.string, code .literal.string { color: #0C5404 } +pre.code .name.builtin, code .name.builtin { color: #352B84 } +pre.code .deleted, code .deleted { background-color: #DEB0A1} +pre.code .inserted, code .inserted { background-color: #A3D289} + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +/* "booktabs" style (no vertical lines) */ +table.docutils.booktabs { + border: 0px; + border-top: 2px solid; + border-bottom: 2px solid; + border-collapse: collapse; +} +table.docutils.booktabs * { + border: 0px; +} +table.docutils.booktabs th { + border-bottom: thin solid; + text-align: left; +} + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } diff --git a/python/helpers/py3only/docutils/writers/html4css1/math.css b/python/helpers/py3only/docutils/writers/html4css1/math.css new file mode 100644 index 000000000000..e6eec6565123 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/html4css1/math.css @@ -0,0 +1,276 @@ +/* +* math2html: convert LaTeX equations to HTML output. +* +* Copyright (C) 2009,2010 Alex Fernández +* +* Released under the terms of the `2-Clause BSD license'_, in short: +* Copying and distribution of this file, with or without modification, +* are permitted in any medium without royalty provided the copyright +* notice and this notice are preserved. +* This file is offered as-is, without any warranty. +* +* .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause +* +* Based on eLyXer: convert LyX source files to HTML output. +* http://elyxer.nongnu.org/ +*/ +/* --end-- +* CSS file for LaTeX formulas. +*/ + +/* Formulas */ +.formula { + text-align: center; + font-family: "Droid Serif", "DejaVu Serif", "STIX", serif; + margin: 1.2em 0; +} +span.formula { + white-space: nowrap; +} +div.formula { + padding: 0.5ex; + margin-left: auto; + margin-right: auto; +} + +/* Basic features */ +a.eqnumber { + display: inline-block; + float: right; + clear: right; + font-weight: bold; +} +span.unknown { + color: #800000; +} +span.ignored, span.arraydef { + display: none; +} +.formula i { + letter-spacing: 0.1ex; +} + +/* Alignment */ +.align-left, .align-l { + text-align: left; +} +.align-right, .align-r { + text-align: right; +} +.align-center, .align-c { + text-align: center; +} + +/* Structures */ +span.overline, span.bar { + text-decoration: overline; +} +.fraction, .fullfraction { + display: inline-block; + vertical-align: middle; + text-align: center; +} +.fraction .fraction { + font-size: 80%; + line-height: 100%; +} +span.numerator { + display: block; +} +span.denominator { + display: block; + padding: 0ex; + border-top: thin solid; +} +sup.numerator, sup.unit { + font-size: 70%; + vertical-align: 80%; +} +sub.denominator, sub.unit { + font-size: 70%; + vertical-align: -20%; +} +span.sqrt { + display: inline-block; + vertical-align: middle; + padding: 0.1ex; +} +sup.root { + font-size: 70%; + position: relative; + left: 1.4ex; +} +span.radical { + display: inline-block; + padding: 0ex; + font-size: 150%; + vertical-align: top; +} +span.root { + display: inline-block; + border-top: thin solid; + padding: 0ex; + vertical-align: middle; +} +span.symbol { + line-height: 125%; + font-size: 125%; +} +span.bigsymbol { + line-height: 150%; + font-size: 150%; +} +span.largesymbol { + font-size: 175%; +} +span.hugesymbol { + font-size: 200%; +} +span.scripts { + display: inline-table; + vertical-align: middle; +} +.script { + display: table-row; + text-align: left; + line-height: 150%; +} +span.limits { + display: inline-table; + vertical-align: middle; +} +.limit { + display: table-row; + line-height: 99%; +} +sup.limit, sub.limit { + line-height: 100%; +} +span.symbolover { + display: inline-block; + text-align: center; + position: relative; + float: right; + right: 100%; + bottom: 0.5em; + width: 0px; +} +span.withsymbol { + display: inline-block; +} +span.symbolunder { + display: inline-block; + text-align: center; + position: relative; + float: right; + right: 80%; + top: 0.3em; + width: 0px; +} + +/* Environments */ +span.array, span.bracketcases, span.binomial, span.environment { + display: inline-table; + text-align: center; + border-collapse: collapse; + margin: 0em; + vertical-align: middle; +} +span.arrayrow, span.binomrow { + display: table-row; + padding: 0ex; + border: 0ex; +} +span.arraycell, span.bracket, span.case, span.binomcell, span.environmentcell { + display: table-cell; + padding: 0ex 0.2ex; + line-height: 99%; + border: 0ex; +} +/* +* CSS file for LaTeX formulas, extra stuff: +* binomials, vertical braces, stackrel, fonts and colors. +*/ + +/* Inline binomials */ +span.binom { + display: inline-block; + vertical-align: middle; + text-align: center; + font-size: 80%; +} +span.binomstack { + display: block; + padding: 0em; +} + +/* Over- and underbraces */ +span.overbrace { + border-top: 2pt solid; +} +span.underbrace { + border-bottom: 2pt solid; +} + +/* Stackrel */ +span.stackrel { + display: inline-block; + text-align: center; +} +span.upstackrel { + display: block; + padding: 0em; + font-size: 80%; + line-height: 64%; + position: relative; + top: 0.15em; + +} +span.downstackrel { + display: block; + vertical-align: bottom; + padding: 0em; +} + +/* Fonts */ +span.mathsf, span.textsf { + font-style: normal; + font-family: sans-serif; +} +span.mathrm, span.textrm { + font-style: normal; + font-family: serif; +} +span.text, span.textnormal { + font-style: normal; +} +span.textipa { + color: #008080; +} +span.fraktur { + font-family: "Lucida Blackletter", eufm10, blackletter; +} +span.blackboard { + font-family: Blackboard, msbm10, serif; +} +span.scriptfont { + font-family: "Monotype Corsiva", "Apple Chancery", "URW Chancery L", cursive; + font-style: italic; +} + +/* Colors */ +span.colorbox { + display: inline-block; + padding: 5px; +} +span.fbox { + display: inline-block; + border: thin solid black; + padding: 2px; +} +span.boxed, span.framebox { + display: inline-block; + border: thin solid black; + padding: 5px; +} + diff --git a/python/helpers/py3only/docutils/writers/html4css1/template.txt b/python/helpers/py3only/docutils/writers/html4css1/template.txt new file mode 100644 index 000000000000..2591bce35df2 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/html4css1/template.txt @@ -0,0 +1,8 @@ +%(head_prefix)s +%(head)s +%(stylesheet)s +%(body_prefix)s +%(body_pre_docinfo)s +%(docinfo)s +%(body)s +%(body_suffix)s diff --git a/python/helpers/py3only/docutils/writers/latex2e/__init__.py b/python/helpers/py3only/docutils/writers/latex2e/__init__.py new file mode 100644 index 000000000000..e5f5a0be5fd8 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/latex2e/__init__.py @@ -0,0 +1,3084 @@ +# .. coding: utf-8 +# $Id: __init__.py 7745 2014-02-28 14:15:59Z milde $ +# Author: Engelbert Gruber, Günter Milde +# Maintainer: docutils-develop@lists.sourceforge.net +# Copyright: This module has been placed in the public domain. + +"""LaTeX2e document tree Writer.""" + +__docformat__ = 'reStructuredText' + +# code contributions from several people included, thanks to all. +# some named: David Abrahams, Julien Letessier, Lele Gaifax, and others. +# +# convention deactivate code by two # i.e. ##. + +import os +import re +import string +import sys + +import urllib.error +import urllib.parse +import urllib.request + +try: + import roman +except ImportError: + import docutils.utils.roman as roman +from docutils import frontend, nodes, languages, writers, utils, io +from docutils.utils.error_reporting import SafeString +from docutils.transforms import writer_aux +from docutils.utils.math import pick_math_environment, unichar2tex + +class Writer(writers.Writer): + + supported = ('latex','latex2e') + """Formats this writer supports.""" + + default_template = 'default.tex' + default_template_path = os.path.dirname(__file__) + + default_preamble = '\n'.join([r'% PDF Standard Fonts', + r'\usepackage{mathptmx} % Times', + r'\usepackage[scaled=.90]{helvet}', + r'\usepackage{courier}']) + settings_spec = ( + 'LaTeX-Specific Options', + None, + (('Specify documentclass. Default is "article".', + ['--documentclass'], + {'default': 'article', }), + ('Specify document options. Multiple options can be given, ' + 'separated by commas. Default is "a4paper".', + ['--documentoptions'], + {'default': 'a4paper', }), + ('Footnotes with numbers/symbols by Docutils. (default)', + ['--docutils-footnotes'], + {'default': True, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Alias for --docutils-footnotes (deprecated)', + ['--use-latex-footnotes'], + {'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Use figure floats for footnote text (deprecated)', + ['--figure-footnotes'], + {'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Format for footnote references: one of "superscript" or ' + '"brackets". Default is "superscript".', + ['--footnote-references'], + {'choices': ['superscript', 'brackets'], 'default': 'superscript', + 'metavar': '', + 'overrides': 'trim_footnote_reference_space'}), + ('Use \\cite command for citations. ', + ['--use-latex-citations'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Use figure floats for citations ' + '(might get mixed with real figures). (default)', + ['--figure-citations'], + {'dest': 'use_latex_citations', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Format for block quote attributions: one of "dash" (em-dash ' + 'prefix), "parentheses"/"parens", or "none". Default is "dash".', + ['--attribution'], + {'choices': ['dash', 'parentheses', 'parens', 'none'], + 'default': 'dash', 'metavar': ''}), + ('Specify LaTeX packages/stylesheets. ' + ' A style is referenced with \\usepackage if extension is ' + '".sty" or omitted and with \\input else. ' + ' Overrides previous --stylesheet and --stylesheet-path settings.', + ['--stylesheet'], + {'default': '', 'metavar': '', + 'overrides': 'stylesheet_path', + 'validator': frontend.validate_comma_separated_list}), + ('Comma separated list of LaTeX packages/stylesheets. ' + 'Relative paths are expanded if a matching file is found in ' + 'the --stylesheet-dirs. With --link-stylesheet, ' + 'the path is rewritten relative to the output *.tex file. ', + ['--stylesheet-path'], + {'metavar': '', 'overrides': 'stylesheet', + 'validator': frontend.validate_comma_separated_list}), + ('Link to the stylesheet(s) in the output file. (default)', + ['--link-stylesheet'], + {'dest': 'embed_stylesheet', 'action': 'store_false'}), + ('Embed the stylesheet(s) in the output file. ' + 'Stylesheets must be accessible during processing. ', + ['--embed-stylesheet'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Comma-separated list of directories where stylesheets are found. ' + 'Used by --stylesheet-path when expanding relative path arguments. ' + 'Default: "."', + ['--stylesheet-dirs'], + {'metavar': '', + 'validator': frontend.validate_comma_separated_list, + 'default': ['.']}), + ('Customization by LaTeX code in the preamble. ' + 'Default: select PDF standard fonts (Times, Helvetica, Courier).', + ['--latex-preamble'], + {'default': default_preamble}), + ('Specify the template file. Default: "%s".' % default_template, + ['--template'], + {'default': default_template, 'metavar': ''}), + ('Table of contents by LaTeX. (default) ', + ['--use-latex-toc'], + {'default': 1, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Table of contents by Docutils (without page numbers). ', + ['--use-docutils-toc'], + {'dest': 'use_latex_toc', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Add parts on top of the section hierarchy.', + ['--use-part-section'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Attach author and date to the document info table. (default) ', + ['--use-docutils-docinfo'], + {'dest': 'use_latex_docinfo', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Attach author and date to the document title.', + ['--use-latex-docinfo'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ("Typeset abstract as topic. (default)", + ['--topic-abstract'], + {'dest': 'use_latex_abstract', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ("Use LaTeX abstract environment for the document's abstract. ", + ['--use-latex-abstract'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Color of any hyperlinks embedded in text ' + '(default: "blue", "false" to disable).', + ['--hyperlink-color'], {'default': 'blue'}), + ('Additional options to the "hyperref" package ' + '(default: "").', + ['--hyperref-options'], {'default': ''}), + ('Enable compound enumerators for nested enumerated lists ' + '(e.g. "1.2.a.ii"). Default: disabled.', + ['--compound-enumerators'], + {'default': None, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable compound enumerators for nested enumerated lists. ' + 'This is the default.', + ['--no-compound-enumerators'], + {'action': 'store_false', 'dest': 'compound_enumerators'}), + ('Enable section ("." subsection ...) prefixes for compound ' + 'enumerators. This has no effect without --compound-enumerators.' + 'Default: disabled.', + ['--section-prefix-for-enumerators'], + {'default': None, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable section prefixes for compound enumerators. ' + 'This is the default.', + ['--no-section-prefix-for-enumerators'], + {'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}), + ('Set the separator between section number and enumerator ' + 'for compound enumerated lists. Default is "-".', + ['--section-enumerator-separator'], + {'default': '-', 'metavar': ''}), + ('When possibile, use the specified environment for literal-blocks. ' + 'Default is quoting of whitespace and special chars.', + ['--literal-block-env'], + {'default': ''}), + ('When possibile, use verbatim for literal-blocks. ' + 'Compatibility alias for "--literal-block-env=verbatim".', + ['--use-verbatim-when-possible'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Table style. "standard" with horizontal and vertical lines, ' + '"booktabs" (LaTeX booktabs style) only horizontal lines ' + 'above and below the table and below the header or "borderless". ' + 'Default: "standard"', + ['--table-style'], + {'choices': ['standard', 'booktabs','nolines', 'borderless'], + 'default': 'standard', + 'metavar': ''}), + ('LaTeX graphicx package option. ' + 'Possible values are "dvips", "pdftex". "auto" includes LaTeX code ' + 'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. ' + 'Default is no option.', + ['--graphicx-option'], + {'default': ''}), + ('LaTeX font encoding. ' + 'Possible values are "", "T1" (default), "OT1", "LGR,T1" or ' + 'any other combination of options to the `fontenc` package. ', + ['--font-encoding'], + {'default': 'T1'}), + ('Per default the latex-writer puts the reference title into ' + 'hyperreferences. Specify "ref*" or "pageref*" to get the section ' + 'number or the page number.', + ['--reference-label'], + {'default': None, }), + ('Specify style and database for bibtex, for example ' + '"--use-bibtex=mystyle,mydb1,mydb2".', + ['--use-bibtex'], + {'default': None, }), + ),) + + settings_defaults = {'sectnum_depth': 0 # updated by SectNum transform + } + config_section = 'latex2e writer' + config_section_dependencies = ('writers',) + + head_parts = ('head_prefix', 'requirements', 'latex_preamble', + 'stylesheet', 'fallbacks', 'pdfsetup', + 'title', 'subtitle', 'titledata') + visitor_attributes = head_parts + ('body_pre_docinfo', 'docinfo', + 'dedication', 'abstract', 'body') + + output = None + """Final translated form of `document`.""" + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = LaTeXTranslator + + # Override parent method to add latex-specific transforms + def get_transforms(self): + return writers.Writer.get_transforms(self) + [ + # Convert specific admonitions to generic one + writer_aux.Admonitions, + # TODO: footnote collection transform + ] + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + # copy parts + for part in self.visitor_attributes: + setattr(self, part, getattr(visitor, part)) + # get template string from file + try: + template_file = open(self.document.settings.template, 'rb') + except IOError: + template_file = open(os.path.join(self.default_template_path, + self.document.settings.template), 'rb') + template = string.Template(str(template_file.read(), 'utf-8')) + template_file.close() + # fill template + self.assemble_parts() # create dictionary of parts + self.output = template.substitute(self.parts) + + def assemble_parts(self): + """Assemble the `self.parts` dictionary of output fragments.""" + writers.Writer.assemble_parts(self) + for part in self.visitor_attributes: + lines = getattr(self, part) + if part in self.head_parts: + if lines: + lines.append('') # to get a trailing newline + self.parts[part] = '\n'.join(lines) + else: + # body contains inline elements, so join without newline + self.parts[part] = ''.join(lines) + + +class Babel(object): + """Language specifics for LaTeX.""" + + # TeX (babel) language names: + # ! not all of these are supported by Docutils! + # + # based on LyX' languages file with adaptions to `BCP 47`_ + # (http://www.rfc-editor.org/rfc/bcp/bcp47.txt) and + # http://www.tug.org/TUGboat/Articles/tb29-3/tb93miklavec.pdf + # * the key without subtags is the default + # * case is ignored + # cf. http://docutils.sourceforge.net/docs/howto/i18n.html + # http://www.w3.org/International/articles/language-tags/ + # and http://www.iana.org/assignments/language-subtag-registry + language_codes = { + # code TeX/Babel-name comment + 'af': 'afrikaans', + 'ar': 'arabic', + # 'be': 'belarusian', + 'bg': 'bulgarian', + 'br': 'breton', + 'ca': 'catalan', + # 'cop': 'coptic', + 'cs': 'czech', + 'cy': 'welsh', + 'da': 'danish', + 'de': 'ngerman', # new spelling (de_1996) + 'de-1901': 'german', # old spelling + 'de-AT': 'naustrian', + 'de-AT-1901': 'austrian', + 'dsb': 'lowersorbian', + 'el': 'greek', # monotonic (el-monoton) + 'el-polyton': 'polutonikogreek', + 'en': 'english', # TeX' default language + 'en-AU': 'australian', + 'en-CA': 'canadian', + 'en-GB': 'british', + 'en-NZ': 'newzealand', + 'en-US': 'american', + 'eo': 'esperanto', + 'es': 'spanish', + 'et': 'estonian', + 'eu': 'basque', + # 'fa': 'farsi', + 'fi': 'finnish', + 'fr': 'french', + 'fr-CA': 'canadien', + 'ga': 'irish', # Irish Gaelic + # 'grc': # Ancient Greek + 'grc-ibycus': 'ibycus', # Ibycus encoding + 'gl': 'galician', + 'he': 'hebrew', + 'hr': 'croatian', + 'hsb': 'uppersorbian', + 'hu': 'magyar', + 'ia': 'interlingua', + 'id': 'bahasai', # Bahasa (Indonesian) + 'is': 'icelandic', + 'it': 'italian', + 'ja': 'japanese', + 'kk': 'kazakh', + 'la': 'latin', + 'lt': 'lithuanian', + 'lv': 'latvian', + 'mn': 'mongolian', # Mongolian, Cyrillic script (mn-cyrl) + 'ms': 'bahasam', # Bahasa (Malay) + 'nb': 'norsk', # Norwegian Bokmal + 'nl': 'dutch', + 'nn': 'nynorsk', # Norwegian Nynorsk + 'no': 'norsk', # Norwegian (Bokmal) + 'pl': 'polish', + 'pt': 'portuges', + 'pt-BR': 'brazil', + 'ro': 'romanian', + 'ru': 'russian', + 'se': 'samin', # North Sami + 'sh-Cyrl': 'serbianc', # Serbo-Croatian, Cyrillic script + 'sh-Latn': 'serbian', # Serbo-Croatian, Latin script see also 'hr' + 'sk': 'slovak', + 'sl': 'slovene', + 'sq': 'albanian', + 'sr': 'serbianc', # Serbian, Cyrillic script (contributed) + 'sr-Latn': 'serbian', # Serbian, Latin script + 'sv': 'swedish', + # 'th': 'thai', + 'tr': 'turkish', + 'uk': 'ukrainian', + 'vi': 'vietnam', + # zh-Latn: Chinese Pinyin + } + # normalize (downcase) keys + language_codes = dict([(k.lower(), v) for (k,v) in list(language_codes.items())]) + + warn_msg = 'Language "%s" not supported by LaTeX (babel)' + + # "Active characters" are shortcuts that start a LaTeX macro and may need + # escaping for literals use. Characters that prevent literal use (e.g. + # starting accent macros like "a -> ä) will be deactivated if one of the + # defining languages is used in the document. + # Special cases: + # ~ (tilde) -- used in estonian, basque, galician, and old versions of + # spanish -- cannot be deactivated as it denotes a no-break space macro, + # " (straight quote) -- used in albanian, austrian, basque + # brazil, bulgarian, catalan, czech, danish, dutch, estonian, + # finnish, galician, german, icelandic, italian, latin, naustrian, + # ngerman, norsk, nynorsk, polish, portuges, russian, serbian, slovak, + # slovene, spanish, swedish, ukrainian, and uppersorbian -- + # is escaped as ``\textquotedbl``. + active_chars = {# TeX/Babel-name: active characters to deactivate + # 'breton': ':;!?' # ensure whitespace + # 'esperanto': '^', + # 'estonian': '~"`', + # 'french': ':;!?' # ensure whitespace + 'galician': '.<>', # also '~"' + # 'magyar': '`', # for special hyphenation cases + 'spanish': '.<>', # old versions also '~' + # 'turkish': ':!=' # ensure whitespace + } + + def __init__(self, language_code, reporter=None): + self.reporter = reporter + self.language = self.language_name(language_code) + self.otherlanguages = {} + + def __call__(self): + """Return the babel call with correct options and settings""" + languages = sorted(self.otherlanguages.keys()) + languages.append(self.language or 'english') + self.setup = [r'\usepackage[%s]{babel}' % ','.join(languages)] + # Deactivate "active characters" + shorthands = [] + for c in ''.join([self.active_chars.get(l, '') for l in languages]): + if c not in shorthands: + shorthands.append(c) + if shorthands: + self.setup.append(r'\AtBeginDocument{\shorthandoff{%s}}' + % ''.join(shorthands)) + # Including '~' in shorthandoff prevents its use as no-break space + if 'galician' in languages: + self.setup.append(r'\deactivatetilden % restore ~ in Galician') + if 'estonian' in languages: + self.setup.extend([r'\makeatletter', + r' \addto\extrasestonian{\bbl@deactivate{~}}', + r'\makeatother']) + if 'basque' in languages: + self.setup.extend([r'\makeatletter', + r' \addto\extrasbasque{\bbl@deactivate{~}}', + r'\makeatother']) + if (languages[-1] == 'english' and + 'french' in list(self.otherlanguages.keys())): + self.setup += ['% Prevent side-effects if French hyphenation ' + 'patterns are not loaded:', + r'\frenchbsetup{StandardLayout}', + r'\AtBeginDocument{\selectlanguage{%s}' + r'\noextrasfrench}' % self.language] + return '\n'.join(self.setup) + + def language_name(self, language_code): + """Return TeX language name for `language_code`""" + for tag in utils.normalize_language_tag(language_code): + try: + return self.language_codes[tag] + except KeyError: + pass + if self.reporter is not None: + self.reporter.warning(self.warn_msg % language_code) + return '' + + def get_language(self): + # Obsolete, kept for backwards compatibility with Sphinx + return self.language + + +# Building blocks for the latex preamble +# -------------------------------------- + +class SortableDict(dict): + """Dictionary with additional sorting methods + + Tip: use key starting with with '_' for sorting before small letters + and with '~' for sorting after small letters. + """ + def sortedkeys(self): + """Return sorted list of keys""" + keys = list(self.keys()) + keys.sort() + return keys + + def sortedvalues(self): + """Return list of values sorted by keys""" + return [self[key] for key in self.sortedkeys()] + + +# PreambleCmds +# ````````````` +# A container for LaTeX code snippets that can be +# inserted into the preamble if required in the document. +# +# .. The package 'makecmds' would enable shorter definitions using the +# \providelength and \provideenvironment commands. +# However, it is pretty non-standard (texlive-latex-extra). + +class PreambleCmds(object): + """Building blocks for the latex preamble.""" + +PreambleCmds.abstract = r""" +% abstract title +\providecommand*{\DUtitleabstract}[1]{\centering\textbf{#1}}""" + +PreambleCmds.admonition = r""" +% admonition (specially marked topic) +\providecommand{\DUadmonition}[2][class-arg]{% + % try \DUadmonition#1{#2}: + \ifcsname DUadmonition#1\endcsname% + \csname DUadmonition#1\endcsname{#2}% + \else + \begin{center} + \fbox{\parbox{0.9\textwidth}{#2}} + \end{center} + \fi +}""" + +PreambleCmds.align_center = r""" +\makeatletter +\@namedef{DUrolealign-center}{\centering} +\makeatother +""" + +## PreambleCmds.caption = r"""% configure caption layout +## \usepackage{caption} +## \captionsetup{singlelinecheck=false}% no exceptions for one-liners""" + +PreambleCmds.color = r"""\usepackage{color}""" + +PreambleCmds.docinfo = r""" +% docinfo (width of docinfo table) +\DUprovidelength{\DUdocinfowidth}{0.9\textwidth}""" +# PreambleCmds.docinfo._depends = 'providelength' + +PreambleCmds.dedication = r""" +% dedication topic +\providecommand{\DUtopicdedication}[1]{\begin{center}#1\end{center}}""" + +PreambleCmds.error = r""" +% error admonition title +\providecommand*{\DUtitleerror}[1]{\DUtitle{\color{red}#1}}""" +# PreambleCmds.errortitle._depends = 'color' + +PreambleCmds.fieldlist = r""" +% fieldlist environment +\ifthenelse{\isundefined{\DUfieldlist}}{ + \newenvironment{DUfieldlist}% + {\quote\description} + {\enddescription\endquote} +}{}""" + +PreambleCmds.float_settings = r"""\usepackage{float} % float configuration +\floatplacement{figure}{H} % place figures here definitely""" + +PreambleCmds.footnotes = r"""% numeric or symbol footnotes with hyperlinks +\providecommand*{\DUfootnotemark}[3]{% + \raisebox{1em}{\hypertarget{#1}{}}% + \hyperlink{#2}{\textsuperscript{#3}}% +} +\providecommand{\DUfootnotetext}[4]{% + \begingroup% + \renewcommand{\thefootnote}{% + \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\hyperlink{#2}{#3}}% + \footnotetext{#4}% + \endgroup% +}""" + +PreambleCmds.footnote_floats = r"""% settings for footnotes as floats: +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50}""" + +PreambleCmds.graphicx_auto = r"""% Check output format +\ifx\pdftexversion\undefined + \usepackage{graphicx} +\else + \usepackage[pdftex]{graphicx} +\fi""" + +PreambleCmds.highlight_rules = r"""% basic code highlight: +\providecommand*\DUrolecomment[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\providecommand*\DUroledeleted[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\providecommand*\DUrolekeyword[1]{\textbf{#1}} +\providecommand*\DUrolestring[1]{\textit{#1}}""" + +PreambleCmds.inline = r""" +% inline markup (custom roles) +% \DUrole{#1}{#2} tries \DUrole#1{#2} +\providecommand*{\DUrole}[2]{% + \ifcsname DUrole#1\endcsname% + \csname DUrole#1\endcsname{#2}% + \else% backwards compatibility: try \docutilsrole#1{#2} + \ifcsname docutilsrole#1\endcsname% + \csname docutilsrole#1\endcsname{#2}% + \else% + #2% + \fi% + \fi% +}""" + +PreambleCmds.legend = r""" +% legend environment +\ifthenelse{\isundefined{\DUlegend}}{ + \newenvironment{DUlegend}{\small}{} +}{}""" + +PreambleCmds.lineblock = r""" +% lineblock environment +\DUprovidelength{\DUlineblockindent}{2.5em} +\ifthenelse{\isundefined{\DUlineblock}}{ + \newenvironment{DUlineblock}[1]{% + \list{}{\setlength{\partopsep}{\parskip} + \addtolength{\partopsep}{\baselineskip} + \setlength{\topsep}{0pt} + \setlength{\itemsep}{0.15\baselineskip} + \setlength{\parsep}{0pt} + \setlength{\leftmargin}{#1}} + \raggedright + } + {\endlist} +}{}""" +# PreambleCmds.lineblock._depends = 'providelength' + +PreambleCmds.linking = r""" +%% hyperlinks: +\ifthenelse{\isundefined{\hypersetup}}{ + \usepackage[%s]{hyperref} + \urlstyle{same} %% normal text font (alternatives: tt, rm, sf) +}{}""" + +PreambleCmds.minitoc = r"""%% local table of contents +\usepackage{minitoc}""" + +PreambleCmds.optionlist = r""" +% optionlist environment +\providecommand*{\DUoptionlistlabel}[1]{\bf #1 \hfill} +\DUprovidelength{\DUoptionlistindent}{3cm} +\ifthenelse{\isundefined{\DUoptionlist}}{ + \newenvironment{DUoptionlist}{% + \list{}{\setlength{\labelwidth}{\DUoptionlistindent} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\DUoptionlistlabel}} + } + {\endlist} +}{}""" +# PreambleCmds.optionlist._depends = 'providelength' + +PreambleCmds.providelength = r""" +% providelength (provide a length variable and set default, if it is new) +\providecommand*{\DUprovidelength}[2]{ + \ifthenelse{\isundefined{#1}}{\newlength{#1}\setlength{#1}{#2}}{} +}""" + +PreambleCmds.rubric = r""" +% rubric (informal heading) +\providecommand*{\DUrubric}[2][class-arg]{% + \subsubsection*{\centering\textit{\textmd{#2}}}}""" + +PreambleCmds.sidebar = r""" +% sidebar (text outside the main text flow) +\providecommand{\DUsidebar}[2][class-arg]{% + \begin{center} + \colorbox[gray]{0.80}{\parbox{0.9\textwidth}{#2}} + \end{center} +}""" + +PreambleCmds.subtitle = r""" +% subtitle (for topic/sidebar) +\providecommand*{\DUsubtitle}[2][class-arg]{\par\emph{#2}\smallskip}""" + +PreambleCmds.documentsubtitle = r""" +% subtitle (in document title) +\providecommand*{\DUdocumentsubtitle}[1]{{\large #1}}""" + +PreambleCmds.table = r"""\usepackage{longtable,ltcaption,array} +\setlength{\extrarowheight}{2pt} +\newlength{\DUtablewidth} % internal use in tables""" + +# Options [force,almostfull] prevent spurious error messages, see +# de.comp.text.tex/2005-12/msg01855 +PreambleCmds.textcomp = """\ +\\usepackage{textcomp} % text symbol macros""" + +PreambleCmds.titlereference = r""" +% titlereference role +\providecommand*{\DUroletitlereference}[1]{\textsl{#1}}""" + +PreambleCmds.title = r""" +% title for topics, admonitions, unsupported section levels, and sidebar +\providecommand*{\DUtitle}[2][class-arg]{% + % call \DUtitle#1{#2} if it exists: + \ifcsname DUtitle#1\endcsname% + \csname DUtitle#1\endcsname{#2}% + \else + \smallskip\noindent\textbf{#2}\smallskip% + \fi +}""" + +PreambleCmds.topic = r""" +% topic (quote with heading) +\providecommand{\DUtopic}[2][class-arg]{% + \ifcsname DUtopic#1\endcsname% + \csname DUtopic#1\endcsname{#2}% + \else + \begin{quote}#2\end{quote} + \fi +}""" + +PreambleCmds.transition = r""" +% transition (break, fancybreak, anonymous section) +\providecommand*{\DUtransition}[1][class-arg]{% + \hspace*{\fill}\hrulefill\hspace*{\fill} + \vskip 0.5\baselineskip +}""" + + +# LaTeX encoding maps +# ------------------- +# :: + +class CharMaps(object): + """LaTeX representations for active and Unicode characters.""" + + # characters that always need escaping: + special = { + ord('#'): r'\#', + ord('$'): r'\$', + ord('%'): r'\%', + ord('&'): r'\&', + ord('~'): r'\textasciitilde{}', + ord('_'): r'\_', + ord('^'): r'\textasciicircum{}', + ord('\\'): r'\textbackslash{}', + ord('{'): r'\{', + ord('}'): r'\}', + # straight double quotes are 'active' in many languages + ord('"'): r'\textquotedbl{}', + # Square brackets are ordinary chars and cannot be escaped with '\', + # so we put them in a group '{[}'. (Alternative: ensure that all + # macros with optional arguments are terminated with {} and text + # inside any optional argument is put in a group ``[{text}]``). + # Commands with optional args inside an optional arg must be put in a + # group, e.g. ``\item[{\hyperref[label]{text}}]``. + ord('['): r'{[}', + ord(']'): r'{]}', + # the soft hyphen is unknown in 8-bit text + # and not properly handled by XeTeX + 0x00AD: r'\-', # SOFT HYPHEN + } + # Unicode chars that are not recognized by LaTeX's utf8 encoding + unsupported_unicode = { + 0x00A0: r'~', # NO-BREAK SPACE + # TODO: ensure white space also at the beginning of a line? + # 0x00A0: ur'\leavevmode\nobreak\vadjust{}~' + 0x2008: r'\,', # PUNCTUATION SPACE    + 0x2011: r'\hbox{-}', # NON-BREAKING HYPHEN + 0x202F: r'\,', # NARROW NO-BREAK SPACE + 0x21d4: r'$\Leftrightarrow$', + # Docutils footnote symbols: + 0x2660: r'$\spadesuit$', + 0x2663: r'$\clubsuit$', + } + # Unicode chars that are recognized by LaTeX's utf8 encoding + utf8_supported_unicode = { + 0x00AB: r'\guillemotleft', # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x00bb: r'\guillemotright', # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x200C: r'\textcompwordmark', # ZERO WIDTH NON-JOINER + 0x2013: r'\textendash{}', + 0x2014: r'\textemdash{}', + 0x2018: r'\textquoteleft{}', + 0x2019: r'\textquoteright{}', + 0x201A: r'\quotesinglbase{}', # SINGLE LOW-9 QUOTATION MARK + 0x201C: r'\textquotedblleft{}', + 0x201D: r'\textquotedblright{}', + 0x201E: r'\quotedblbase{}', # DOUBLE LOW-9 QUOTATION MARK + 0x2030: r'\textperthousand{}', # PER MILLE SIGN + 0x2031: r'\textpertenthousand{}', # PER TEN THOUSAND SIGN + 0x2039: r'\guilsinglleft{}', + 0x203A: r'\guilsinglright{}', + 0x2423: r'\textvisiblespace{}', # OPEN BOX + 0x2020: r'\dag{}', + 0x2021: r'\ddag{}', + 0x2026: r'\dots{}', + 0x2122: r'\texttrademark{}', + } + # recognized with 'utf8', if textcomp is loaded + textcomp = { + # Latin-1 Supplement + 0x00a2: r'\textcent{}', # ¢ CENT SIGN + 0x00a4: r'\textcurrency{}', # ¤ CURRENCY SYMBOL + 0x00a5: r'\textyen{}', # ¥ YEN SIGN + 0x00a6: r'\textbrokenbar{}', # ¦ BROKEN BAR + 0x00a7: r'\textsection{}', # § SECTION SIGN + 0x00a8: r'\textasciidieresis{}', # ¨ DIAERESIS + 0x00a9: r'\textcopyright{}', # © COPYRIGHT SIGN + 0x00aa: r'\textordfeminine{}', # ª FEMININE ORDINAL INDICATOR + 0x00ac: r'\textlnot{}', # ¬ NOT SIGN + 0x00ae: r'\textregistered{}', # ® REGISTERED SIGN + 0x00af: r'\textasciimacron{}', # ¯ MACRON + 0x00b0: r'\textdegree{}', # ° DEGREE SIGN + 0x00b1: r'\textpm{}', # ± PLUS-MINUS SIGN + 0x00b2: r'\texttwosuperior{}', # ² SUPERSCRIPT TWO + 0x00b3: r'\textthreesuperior{}', # ³ SUPERSCRIPT THREE + 0x00b4: r'\textasciiacute{}', # ´ ACUTE ACCENT + 0x00b5: r'\textmu{}', # µ MICRO SIGN + 0x00b6: r'\textparagraph{}', # ¶ PILCROW SIGN # != \textpilcrow + 0x00b9: r'\textonesuperior{}', # ¹ SUPERSCRIPT ONE + 0x00ba: r'\textordmasculine{}', # º MASCULINE ORDINAL INDICATOR + 0x00bc: r'\textonequarter{}', # 1/4 FRACTION + 0x00bd: r'\textonehalf{}', # 1/2 FRACTION + 0x00be: r'\textthreequarters{}', # 3/4 FRACTION + 0x00d7: r'\texttimes{}', # × MULTIPLICATION SIGN + 0x00f7: r'\textdiv{}', # ÷ DIVISION SIGN + # others + 0x0192: r'\textflorin{}', # LATIN SMALL LETTER F WITH HOOK + 0x02b9: r'\textasciiacute{}', # MODIFIER LETTER PRIME + 0x02ba: r'\textacutedbl{}', # MODIFIER LETTER DOUBLE PRIME + 0x2016: r'\textbardbl{}', # DOUBLE VERTICAL LINE + 0x2022: r'\textbullet{}', # BULLET + 0x2032: r'\textasciiacute{}', # PRIME + 0x2033: r'\textacutedbl{}', # DOUBLE PRIME + 0x2035: r'\textasciigrave{}', # REVERSED PRIME + 0x2036: r'\textgravedbl{}', # REVERSED DOUBLE PRIME + 0x203b: r'\textreferencemark{}', # REFERENCE MARK + 0x203d: r'\textinterrobang{}', # INTERROBANG + 0x2044: r'\textfractionsolidus{}', # FRACTION SLASH + 0x2045: r'\textlquill{}', # LEFT SQUARE BRACKET WITH QUILL + 0x2046: r'\textrquill{}', # RIGHT SQUARE BRACKET WITH QUILL + 0x2052: r'\textdiscount{}', # COMMERCIAL MINUS SIGN + 0x20a1: r'\textcolonmonetary{}', # COLON SIGN + 0x20a3: r'\textfrenchfranc{}', # FRENCH FRANC SIGN + 0x20a4: r'\textlira{}', # LIRA SIGN + 0x20a6: r'\textnaira{}', # NAIRA SIGN + 0x20a9: r'\textwon{}', # WON SIGN + 0x20ab: r'\textdong{}', # DONG SIGN + 0x20ac: r'\texteuro{}', # EURO SIGN + 0x20b1: r'\textpeso{}', # PESO SIGN + 0x20b2: r'\textguarani{}', # GUARANI SIGN + 0x2103: r'\textcelsius{}', # DEGREE CELSIUS + 0x2116: r'\textnumero{}', # NUMERO SIGN + 0x2117: r'\textcircledP{}', # SOUND RECORDING COYRIGHT + 0x211e: r'\textrecipe{}', # PRESCRIPTION TAKE + 0x2120: r'\textservicemark{}', # SERVICE MARK + 0x2122: r'\texttrademark{}', # TRADE MARK SIGN + 0x2126: r'\textohm{}', # OHM SIGN + 0x2127: r'\textmho{}', # INVERTED OHM SIGN + 0x212e: r'\textestimated{}', # ESTIMATED SYMBOL + 0x2190: r'\textleftarrow{}', # LEFTWARDS ARROW + 0x2191: r'\textuparrow{}', # UPWARDS ARROW + 0x2192: r'\textrightarrow{}', # RIGHTWARDS ARROW + 0x2193: r'\textdownarrow{}', # DOWNWARDS ARROW + 0x2212: r'\textminus{}', # MINUS SIGN + 0x2217: r'\textasteriskcentered{}', # ASTERISK OPERATOR + 0x221a: r'\textsurd{}', # SQUARE ROOT + 0x2422: r'\textblank{}', # BLANK SYMBOL + 0x25e6: r'\textopenbullet{}', # WHITE BULLET + 0x25ef: r'\textbigcircle{}', # LARGE CIRCLE + 0x266a: r'\textmusicalnote{}', # EIGHTH NOTE + 0x26ad: r'\textmarried{}', # MARRIAGE SYMBOL + 0x26ae: r'\textdivorced{}', # DIVORCE SYMBOL + 0x27e8: r'\textlangle{}', # MATHEMATICAL LEFT ANGLE BRACKET + 0x27e9: r'\textrangle{}', # MATHEMATICAL RIGHT ANGLE BRACKET + } + # Unicode chars that require a feature/package to render + pifont = { + 0x2665: r'\ding{170}', # black heartsuit + 0x2666: r'\ding{169}', # black diamondsuit + 0x2713: r'\ding{51}', # check mark + 0x2717: r'\ding{55}', # check mark + } + # TODO: greek alphabet ... ? + # see also LaTeX codec + # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124 + # and unimap.py from TeXML + + +class DocumentClass(object): + """Details of a LaTeX document class.""" + + def __init__(self, document_class, with_part=False): + self.document_class = document_class + self._with_part = with_part + self.sections = ['section', 'subsection', 'subsubsection', + 'paragraph', 'subparagraph'] + if self.document_class in ('book', 'memoir', 'report', + 'scrbook', 'scrreprt'): + self.sections.insert(0, 'chapter') + if self._with_part: + self.sections.insert(0, 'part') + + def section(self, level): + """Return the LaTeX section name for section `level`. + + The name depends on the specific document class. + Level is 1,2,3..., as level 0 is the title. + """ + if level <= len(self.sections): + return self.sections[level-1] + else: # unsupported levels + return 'DUtitle[section%s]' % roman.toRoman(level) + +class Table(object): + """Manage a table while traversing. + + Maybe change to a mixin defining the visit/departs, but then + class Table internal variables are in the Translator. + + Table style might be + + :standard: horizontal and vertical lines + :booktabs: only horizontal lines (requires "booktabs" LaTeX package) + :borderless: no borders around table cells + :nolines: alias for borderless + """ + def __init__(self,translator,latex_type,table_style): + self._translator = translator + self._latex_type = latex_type + self._table_style = table_style + self._open = False + # miscellaneous attributes + self._attrs = {} + self._col_width = [] + self._rowspan = [] + self.stubs = [] + self._in_thead = 0 + + def open(self): + self._open = True + self._col_specs = [] + self.caption = [] + self._attrs = {} + self._in_head = False # maybe context with search + def close(self): + self._open = False + self._col_specs = None + self.caption = [] + self._attrs = {} + self.stubs = [] + def is_open(self): + return self._open + + def set_table_style(self, table_style): + if not table_style in ('standard','booktabs','borderless','nolines'): + return + self._table_style = table_style + + def get_latex_type(self): + if self._latex_type == 'longtable' and not self.caption: + # do not advance the "table" counter (requires "ltcaption" package) + return('longtable*') + return self._latex_type + + def set(self,attr,value): + self._attrs[attr] = value + def get(self,attr): + if attr in self._attrs: + return self._attrs[attr] + return None + + def get_vertical_bar(self): + if self._table_style == 'standard': + return '|' + return '' + + # horizontal lines are drawn below a row, + def get_opening(self): + return '\n'.join([r'\setlength{\DUtablewidth}{\linewidth}', + r'\begin{%s}[c]' % self.get_latex_type()]) + + def get_closing(self): + closing = [] + if self._table_style == 'booktabs': + closing.append(r'\bottomrule') + # elif self._table_style == 'standard': + # closing.append(r'\hline') + closing.append(r'\end{%s}' % self.get_latex_type()) + return '\n'.join(closing) + + def visit_colspec(self, node): + self._col_specs.append(node) + # "stubs" list is an attribute of the tgroup element: + self.stubs.append(node.attributes.get('stub')) + + def get_colspecs(self): + """Return column specification for longtable. + + Assumes reST line length being 80 characters. + Table width is hairy. + + === === + ABC DEF + === === + + usually gets to narrow, therefore we add 1 (fiddlefactor). + """ + width = 80 + + total_width = 0.0 + # first see if we get too wide. + for node in self._col_specs: + colwidth = float(node['colwidth']+1) / width + total_width += colwidth + self._col_width = [] + self._rowspan = [] + # donot make it full linewidth + factor = 0.93 + if total_width > 1.0: + factor /= total_width + bar = self.get_vertical_bar() + latex_table_spec = '' + for node in self._col_specs: + colwidth = factor * float(node['colwidth']+1) / width + self._col_width.append(colwidth+0.005) + self._rowspan.append(0) + latex_table_spec += '%sp{%.3f\\DUtablewidth}' % (bar, colwidth+0.005) + return latex_table_spec+bar + + def get_column_width(self): + """Return columnwidth for current cell (not multicell).""" + return '%.2f\\DUtablewidth' % self._col_width[self._cell_in_row-1] + + def get_multicolumn_width(self, start, len_): + """Return sum of columnwidths for multicell.""" + mc_width = sum([width + for width in ([self._col_width[start + co - 1] + for co in range (len_)])]) + return '%.2f\\DUtablewidth' % mc_width + + def get_caption(self): + if not self.caption: + return '' + caption = ''.join(self.caption) + if 1 == self._translator.thead_depth(): + return r'\caption{%s}\\' '\n' % caption + return r'\caption[]{%s (... continued)}\\' '\n' % caption + + def need_recurse(self): + if self._latex_type == 'longtable': + return 1 == self._translator.thead_depth() + return 0 + + def visit_thead(self): + self._in_thead += 1 + if self._table_style == 'standard': + return ['\\hline\n'] + elif self._table_style == 'booktabs': + return ['\\toprule\n'] + return [] + + def depart_thead(self): + a = [] + #if self._table_style == 'standard': + # a.append('\\hline\n') + if self._table_style == 'booktabs': + a.append('\\midrule\n') + if self._latex_type == 'longtable': + if 1 == self._translator.thead_depth(): + a.append('\\endfirsthead\n') + else: + a.append('\\endhead\n') + a.append(r'\multicolumn{%d}{c}' % len(self._col_specs) + + r'{\hfill ... continued on next page} \\') + a.append('\n\\endfoot\n\\endlastfoot\n') + # for longtable one could add firsthead, foot and lastfoot + self._in_thead -= 1 + return a + def visit_row(self): + self._cell_in_row = 0 + def depart_row(self): + res = [' \\\\\n'] + self._cell_in_row = None # remove cell counter + for i in range(len(self._rowspan)): + if (self._rowspan[i]>0): + self._rowspan[i] -= 1 + + if self._table_style == 'standard': + rowspans = [i+1 for i in range(len(self._rowspan)) + if (self._rowspan[i]<=0)] + if len(rowspans)==len(self._rowspan): + res.append('\\hline\n') + else: + cline = '' + rowspans.reverse() + # TODO merge clines + while True: + try: + c_start = rowspans.pop() + except: + break + cline += '\\cline{%d-%d}\n' % (c_start,c_start) + res.append(cline) + return res + + def set_rowspan(self,cell,value): + try: + self._rowspan[cell] = value + except: + pass + def get_rowspan(self,cell): + try: + return self._rowspan[cell] + except: + return 0 + def get_entry_number(self): + return self._cell_in_row + def visit_entry(self): + self._cell_in_row += 1 + def is_stub_column(self): + if len(self.stubs) >= self._cell_in_row: + return self.stubs[self._cell_in_row-1] + return False + + +class LaTeXTranslator(nodes.NodeVisitor): + + # When options are given to the documentclass, latex will pass them + # to other packages, as done with babel. + # Dummy settings might be taken from document settings + + # Write code for typesetting with 8-bit tex/pdftex (vs. xetex/luatex) engine + # overwritten by the XeTeX writer + is_xetex = False + + # Config setting defaults + # ----------------------- + + # TODO: use mixins for different implementations. + # list environment for docinfo. else tabularx + ## use_optionlist_for_docinfo = False # TODO: NOT YET IN USE + + # Use compound enumerations (1.A.1.) + compound_enumerators = False + + # If using compound enumerations, include section information. + section_prefix_for_enumerators = False + + # This is the character that separates the section ("." subsection ...) + # prefix from the regular list enumerator. + section_enumerator_separator = '-' + + # Auxiliary variables + # ------------------- + + has_latex_toc = False # is there a toc in the doc? (needed by minitoc) + is_toc_list = False # is the current bullet_list a ToC? + section_level = 0 + + # Flags to encode(): + # inside citation reference labels underscores dont need to be escaped + inside_citation_reference_label = False + verbatim = False # do not encode + insert_non_breaking_blanks = False # replace blanks by "~" + insert_newline = False # add latex newline commands + literal = False # literal text (block or inline) + + + def __init__(self, document, babel_class=Babel): + nodes.NodeVisitor.__init__(self, document) + # Reporter + # ~~~~~~~~ + self.warn = self.document.reporter.warning + self.error = self.document.reporter.error + + # Settings + # ~~~~~~~~ + self.settings = settings = document.settings + self.latex_encoding = self.to_latex_encoding(settings.output_encoding) + self.use_latex_toc = settings.use_latex_toc + self.use_latex_docinfo = settings.use_latex_docinfo + self._use_latex_citations = settings.use_latex_citations + self._reference_label = settings.reference_label + self.hyperlink_color = settings.hyperlink_color + self.compound_enumerators = settings.compound_enumerators + self.font_encoding = getattr(settings, 'font_encoding', '') + self.section_prefix_for_enumerators = ( + settings.section_prefix_for_enumerators) + self.section_enumerator_separator = ( + settings.section_enumerator_separator.replace('_', r'\_')) + # literal blocks: + self.literal_block_env = '' + self.literal_block_options = '' + if settings.literal_block_env != '': + (none, + self.literal_block_env, + self.literal_block_options, + none ) = re.split('(\w+)(.*)', settings.literal_block_env) + elif settings.use_verbatim_when_possible: + self.literal_block_env = 'verbatim' + # + if self.settings.use_bibtex: + self.bibtex = self.settings.use_bibtex.split(',',1) + # TODO avoid errors on not declared citations. + else: + self.bibtex = None + # language module for Docutils-generated text + # (labels, bibliographic_fields, and author_separators) + self.language_module = languages.get_language(settings.language_code, + document.reporter) + self.babel = babel_class(settings.language_code, document.reporter) + self.author_separator = self.language_module.author_separators[0] + d_options = [self.settings.documentoptions] + if self.babel.language not in ('english', ''): + d_options.append(self.babel.language) + self.documentoptions = ','.join([_f for _f in d_options if _f]) + self.d_class = DocumentClass(settings.documentclass, + settings.use_part_section) + # graphic package options: + if self.settings.graphicx_option == '': + self.graphicx_package = r'\usepackage{graphicx}' + elif self.settings.graphicx_option.lower() == 'auto': + self.graphicx_package = PreambleCmds.graphicx_auto + else: + self.graphicx_package = (r'\usepackage[%s]{graphicx}' % + self.settings.graphicx_option) + # footnotes: + self.docutils_footnotes = settings.docutils_footnotes + if settings.use_latex_footnotes: + self.docutils_footnotes = True + self.warn('`use_latex_footnotes` is deprecated. ' + 'The setting has been renamed to `docutils_footnotes` ' + 'and the alias will be removed in a future version.') + self.figure_footnotes = settings.figure_footnotes + if self.figure_footnotes: + self.docutils_footnotes = True + self.warn('The "figure footnotes" workaround/setting is strongly ' + 'deprecated and will be removed in a future version.') + + # Output collection stacks + # ~~~~~~~~~~~~~~~~~~~~~~~~ + + # Document parts + self.head_prefix = [r'\documentclass[%s]{%s}' % + (self.documentoptions, self.settings.documentclass)] + self.requirements = SortableDict() # made a list in depart_document() + self.requirements['__static'] = r'\usepackage{ifthen}' + self.latex_preamble = [settings.latex_preamble] + self.fallbacks = SortableDict() # made a list in depart_document() + self.pdfsetup = [] # PDF properties (hyperref package) + self.title = [] + self.subtitle = [] + self.titledata = [] # \title, \author, \date + ## self.body_prefix = ['\\begin{document}\n'] + self.body_pre_docinfo = [] # \maketitle + self.docinfo = [] + self.dedication = [] + self.abstract = [] + self.body = [] + ## self.body_suffix = ['\\end{document}\n'] + + # A heterogenous stack used in conjunction with the tree traversal. + # Make sure that the pops correspond to the pushes: + self.context = [] + + # Title metadata: + self.title_labels = [] + self.subtitle_labels = [] + # (if use_latex_docinfo: collects lists of + # author/organization/contact/address lines) + self.author_stack = [] + self.date = [] + + # PDF properties: pdftitle, pdfauthor + # TODO?: pdfcreator, pdfproducer, pdfsubject, pdfkeywords + self.pdfinfo = [] + self.pdfauthor = [] + + # Stack of section counters so that we don't have to use_latex_toc. + # This will grow and shrink as processing occurs. + # Initialized for potential first-level sections. + self._section_number = [0] + + # The current stack of enumerations so that we can expand + # them into a compound enumeration. + self._enumeration_counters = [] + # The maximum number of enumeration counters we've used. + # If we go beyond this number, we need to create a new + # counter; otherwise, just reuse an old one. + self._max_enumeration_counters = 0 + + self._bibitems = [] + + # object for a table while proccessing. + self.table_stack = [] + self.active_table = Table(self, 'longtable', settings.table_style) + + # Where to collect the output of visitor methods (default: body) + self.out = self.body + self.out_stack = [] # stack of output collectors + + # Process settings + # ~~~~~~~~~~~~~~~~ + # Encodings: + # Docutils' output-encoding => TeX input encoding + if self.latex_encoding != 'ascii': + self.requirements['_inputenc'] = (r'\usepackage[%s]{inputenc}' + % self.latex_encoding) + # TeX font encoding + if not self.is_xetex: + if self.font_encoding: + self.requirements['_fontenc'] = (r'\usepackage[%s]{fontenc}' % + self.font_encoding) + # ensure \textquotedbl is defined: + for enc in self.font_encoding.split(','): + enc = enc.strip() + if enc == 'OT1': + self.requirements['_textquotedblOT1'] = ( + r'\DeclareTextSymbol{\textquotedbl}{OT1}{`\"}') + elif enc not in ('T1', 'T2A', 'T2B', 'T2C', 'T4', 'T5'): + self.requirements['_textquotedbl'] = ( + r'\DeclareTextSymbolDefault{\textquotedbl}{T1}') + # page layout with typearea (if there are relevant document options) + if (settings.documentclass.find('scr') == -1 and + (self.documentoptions.find('DIV') != -1 or + self.documentoptions.find('BCOR') != -1)): + self.requirements['typearea'] = r'\usepackage{typearea}' + + # Stylesheets + # (the name `self.stylesheet` is singular because only one + # stylesheet was supported before Docutils 0.6). + self.stylesheet = [self.stylesheet_call(path) + for path in utils.get_stylesheet_list(settings)] + + # PDF setup + if self.hyperlink_color in ('0', 'false', 'False', ''): + self.hyperref_options = '' + else: + self.hyperref_options = 'colorlinks=true,linkcolor=%s,urlcolor=%s' % ( + self.hyperlink_color, self.hyperlink_color) + if settings.hyperref_options: + self.hyperref_options += ',' + settings.hyperref_options + + # LaTeX Toc + # include all supported sections in toc and PDF bookmarks + # (or use documentclass-default (as currently))? + ## if self.use_latex_toc: + ## self.requirements['tocdepth'] = (r'\setcounter{tocdepth}{%d}' % + ## len(self.d_class.sections)) + + # Section numbering + if settings.sectnum_xform: # section numbering by Docutils + PreambleCmds.secnumdepth = r'\setcounter{secnumdepth}{0}' + else: # section numbering by LaTeX: + secnumdepth = settings.sectnum_depth + # Possible values of settings.sectnum_depth: + # None "sectnum" directive without depth arg -> LaTeX default + # 0 no "sectnum" directive -> no section numbers + # >0 value of "depth" argument -> translate to LaTeX levels: + # -1 part (0 with "article" document class) + # 0 chapter (missing in "article" document class) + # 1 section + # 2 subsection + # 3 subsubsection + # 4 paragraph + # 5 subparagraph + if secnumdepth is not None: + # limit to supported levels + secnumdepth = min(secnumdepth, len(self.d_class.sections)) + # adjust to document class and use_part_section settings + if 'chapter' in self.d_class.sections: + secnumdepth -= 1 + if self.d_class.sections[0] == 'part': + secnumdepth -= 1 + PreambleCmds.secnumdepth = \ + r'\setcounter{secnumdepth}{%d}' % secnumdepth + + # start with specified number: + if (hasattr(settings, 'sectnum_start') and + settings.sectnum_start != 1): + self.requirements['sectnum_start'] = ( + r'\setcounter{%s}{%d}' % (self.d_class.sections[0], + settings.sectnum_start-1)) + # TODO: currently ignored (configure in a stylesheet): + ## settings.sectnum_prefix + ## settings.sectnum_suffix + + # Auxiliary Methods + # ----------------- + + def stylesheet_call(self, path): + """Return code to reference or embed stylesheet file `path`""" + # is it a package (no extension or *.sty) or "normal" tex code: + (base, ext) = os.path.splitext(path) + is_package = ext in ['.sty', ''] + # Embed content of style file: + if self.settings.embed_stylesheet: + if is_package: + path = base + '.sty' # ensure extension + try: + content = io.FileInput(source_path=path, + encoding='utf-8').read() + self.settings.record_dependencies.add(path) + except IOError as err: + msg = "Cannot embed stylesheet '%s':\n %s." % ( + path, SafeString(err.strerror)) + self.document.reporter.error(msg) + return '% ' + msg.replace('\n', '\n% ') + if is_package: + content = '\n'.join([r'\makeatletter', + content, + r'\makeatother']) + return '%% embedded stylesheet: %s\n%s' % (path, content) + # Link to style file: + if is_package: + path = base # drop extension + cmd = r'\usepackage{%s}' + else: + cmd = r'\input{%s}' + if self.settings.stylesheet_path: + # adapt path relative to output (cf. config.html#stylesheet-path) + path = utils.relative_path(self.settings._destination, path) + return cmd % path + + def to_latex_encoding(self,docutils_encoding): + """Translate docutils encoding name into LaTeX's. + + Default method is remove "-" and "_" chars from docutils_encoding. + """ + tr = { 'iso-8859-1': 'latin1', # west european + 'iso-8859-2': 'latin2', # east european + 'iso-8859-3': 'latin3', # esperanto, maltese + 'iso-8859-4': 'latin4', # north european, scandinavian, baltic + 'iso-8859-5': 'iso88595', # cyrillic (ISO) + 'iso-8859-9': 'latin5', # turkish + 'iso-8859-15': 'latin9', # latin9, update to latin1. + 'mac_cyrillic': 'maccyr', # cyrillic (on Mac) + 'windows-1251': 'cp1251', # cyrillic (on Windows) + 'koi8-r': 'koi8-r', # cyrillic (Russian) + 'koi8-u': 'koi8-u', # cyrillic (Ukrainian) + 'windows-1250': 'cp1250', # + 'windows-1252': 'cp1252', # + 'us-ascii': 'ascii', # ASCII (US) + # unmatched encodings + #'': 'applemac', + #'': 'ansinew', # windows 3.1 ansi + #'': 'ascii', # ASCII encoding for the range 32--127. + #'': 'cp437', # dos latin us + #'': 'cp850', # dos latin 1 + #'': 'cp852', # dos latin 2 + #'': 'decmulti', + #'': 'latin10', + #'iso-8859-6': '' # arabic + #'iso-8859-7': '' # greek + #'iso-8859-8': '' # hebrew + #'iso-8859-10': '' # latin6, more complete iso-8859-4 + } + encoding = docutils_encoding.lower() + if encoding in tr: + return tr[encoding] + # drop hyphen or low-line from "latin-1", "latin_1", "utf-8" and similar + encoding = encoding.replace('_', '').replace('-', '') + # strip the error handler + return encoding.split(':')[0] + + def language_label(self, docutil_label): + return self.language_module.labels[docutil_label] + + def encode(self, text): + """Return text with 'problematic' characters escaped. + + * Escape the ten special printing characters ``# $ % & ~ _ ^ \ { }``, + square brackets ``[ ]``, double quotes and (in OT1) ``< | >``. + * Translate non-supported Unicode characters. + * Separate ``-`` (and more in literal text) to prevent input ligatures. + """ + if self.verbatim: + return text + + # Set up the translation table: + table = CharMaps.special.copy() + # keep the underscore in citation references + if self.inside_citation_reference_label: + del(table[ord('_')]) + # Workarounds for OT1 font-encoding + if self.font_encoding in ['OT1', ''] and not self.is_xetex: + # * out-of-order characters in cmtt + if self.literal: + # replace underscore by underlined blank, + # because this has correct width. + table[ord('_')] = '\\underline{~}' + # the backslash doesn't work, so we use a mirrored slash. + # \reflectbox is provided by graphicx: + self.requirements['graphicx'] = self.graphicx_package + table[ord('\\')] = r'\reflectbox{/}' + # * ``< | >`` come out as different chars (except for cmtt): + else: + table[ord('|')] = r'\textbar{}' + table[ord('<')] = r'\textless{}' + table[ord('>')] = r'\textgreater{}' + if self.insert_non_breaking_blanks: + table[ord(' ')] = r'~' + # Unicode replacements for 8-bit tex engines (not required with XeTeX/LuaTeX): + if not self.is_xetex: + table.update(CharMaps.unsupported_unicode) + if not self.latex_encoding.startswith('utf8'): + table.update(CharMaps.utf8_supported_unicode) + table.update(CharMaps.textcomp) + table.update(CharMaps.pifont) + # Characters that require a feature/package to render + if [True for ch in text if ord(ch) in CharMaps.textcomp]: + self.requirements['textcomp'] = PreambleCmds.textcomp + if [True for ch in text if ord(ch) in CharMaps.pifont]: + self.requirements['pifont'] = '\\usepackage{pifont}' + + text = text.translate(table) + + # Break up input ligatures e.g. '--' to '-{}-'. + if not self.is_xetex: # Not required with xetex/luatex + separate_chars = '-' + # In monospace-font, we also separate ',,', '``' and "''" and some + # other characters which can't occur in non-literal text. + if self.literal: + separate_chars += ',`\'"<>' + for char in separate_chars * 2: + # Do it twice ("* 2") because otherwise we would replace + # '---' by '-{}--'. + text = text.replace(char + char, char + '{}' + char) + + # Literal line breaks (in address or literal blocks): + if self.insert_newline: + lines = text.split('\n') + # Add a protected space to blank lines (except the last) + # to avoid ``! LaTeX Error: There's no line here to end.`` + for i, line in enumerate(lines[:-1]): + if not line.lstrip(): + lines[i] += '~' + text = (r'\\' + '\n').join(lines) + if self.literal and not self.insert_non_breaking_blanks: + # preserve runs of spaces but allow wrapping + text = text.replace(' ', ' ~') + return text + + def attval(self, text, + whitespace=re.compile('[\n\r\t\v\f]')): + """Cleanse, encode, and return attribute value text.""" + return self.encode(whitespace.sub(' ', text)) + + # TODO: is this used anywhere? -> update (use template) or delete + ## def astext(self): + ## """Assemble document parts and return as string.""" + ## head = '\n'.join(self.head_prefix + self.stylesheet + self.head) + ## body = ''.join(self.body_prefix + self.body + self.body_suffix) + ## return head + '\n' + body + + def is_inline(self, node): + """Check whether a node represents an inline or block-level element""" + return isinstance(node.parent, nodes.TextElement) + + def append_hypertargets(self, node): + """Append hypertargets for all ids of `node`""" + # hypertarget places the anchor at the target's baseline, + # so we raise it explicitely + self.out.append('%\n'.join(['\\raisebox{1em}{\\hypertarget{%s}{}}' % + id for id in node['ids']])) + + def ids_to_labels(self, node, set_anchor=True): + """Return list of label definitions for all ids of `node` + + If `set_anchor` is True, an anchor is set with \phantomsection. + """ + labels = ['\\label{%s}' % id for id in node.get('ids', [])] + if set_anchor and labels: + labels.insert(0, '\\phantomsection') + return labels + + def push_output_collector(self, new_out): + self.out_stack.append(self.out) + self.out = new_out + + def pop_output_collector(self): + self.out = self.out_stack.pop() + + # Visitor methods + # --------------- + + def visit_Text(self, node): + self.out.append(self.encode(node.astext())) + + def depart_Text(self, node): + pass + + def visit_abbreviation(self, node): + node['classes'].insert(0, 'abbreviation') + self.visit_inline(node) + + def depart_abbreviation(self, node): + self.depart_inline(node) + + def visit_acronym(self, node): + node['classes'].insert(0, 'acronym') + self.visit_inline(node) + + def depart_acronym(self, node): + self.depart_inline(node) + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address') + + def depart_address(self, node): + self.depart_docinfo_item(node) + + def visit_admonition(self, node): + self.fallbacks['admonition'] = PreambleCmds.admonition + if 'error' in node['classes']: + self.fallbacks['error'] = PreambleCmds.error + # strip the generic 'admonition' from the list of classes + node['classes'] = [cls for cls in node['classes'] + if cls != 'admonition'] + self.out.append('\n\\DUadmonition[%s]{\n' % ','.join(node['classes'])) + + def depart_admonition(self, node=None): + self.out.append('}\n') + + def visit_author(self, node): + self.visit_docinfo_item(node, 'author') + + def depart_author(self, node): + self.depart_docinfo_item(node) + + def visit_authors(self, node): + # not used: visit_author is called anyway for each author. + pass + + def depart_authors(self, node): + pass + + def visit_block_quote(self, node): + self.out.append( '%\n\\begin{quote}\n') + if node['classes']: + self.visit_inline(node) + + def depart_block_quote(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append( '\n\\end{quote}\n') + + def visit_bullet_list(self, node): + if self.is_toc_list: + self.out.append( '%\n\\begin{list}{}{}\n' ) + else: + self.out.append( '%\n\\begin{itemize}\n' ) + # if node['classes']: + # self.visit_inline(node) + + def depart_bullet_list(self, node): + # if node['classes']: + # self.depart_inline(node) + if self.is_toc_list: + self.out.append( '\n\\end{list}\n' ) + else: + self.out.append( '\n\\end{itemize}\n' ) + + def visit_superscript(self, node): + self.out.append(r'\textsuperscript{') + if node['classes']: + self.visit_inline(node) + + def depart_superscript(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_subscript(self, node): + self.out.append(r'\textsubscript{') # requires `fixltx2e` + if node['classes']: + self.visit_inline(node) + + def depart_subscript(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_caption(self, node): + self.out.append('\n\\caption{') + + def depart_caption(self, node): + self.out.append('}\n') + + def visit_title_reference(self, node): + self.fallbacks['titlereference'] = PreambleCmds.titlereference + self.out.append(r'\DUroletitlereference{') + if node['classes']: + self.visit_inline(node) + + def depart_title_reference(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append( '}' ) + + def visit_citation(self, node): + # TODO maybe use cite bibitems + if self._use_latex_citations: + self.push_output_collector([]) + else: + # TODO: do we need these? + ## self.requirements['~fnt_floats'] = PreambleCmds.footnote_floats + self.out.append(r'\begin{figure}[b]') + self.append_hypertargets(node) + + def depart_citation(self, node): + if self._use_latex_citations: + label = self.out[0] + text = ''.join(self.out[1:]) + self._bibitems.append([label, text]) + self.pop_output_collector() + else: + self.out.append('\\end{figure}\n') + + def visit_citation_reference(self, node): + if self._use_latex_citations: + if not self.inside_citation_reference_label: + self.out.append(r'\cite{') + self.inside_citation_reference_label = 1 + else: + assert self.body[-1] in (' ', '\n'),\ + 'unexpected non-whitespace while in reference label' + del self.body[-1] + else: + href = '' + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + self.out.append('\\hyperlink{%s}{[' % href) + + def depart_citation_reference(self, node): + if self._use_latex_citations: + followup_citation = False + # check for a following citation separated by a space or newline + next_siblings = node.traverse(descend=False, siblings=True, + include_self=False) + if len(next_siblings) > 1: + next = next_siblings[0] + if (isinstance(next, nodes.Text) and + next.astext() in (' ', '\n')): + if next_siblings[1].__class__ == node.__class__: + followup_citation = True + if followup_citation: + self.out.append(',') + else: + self.out.append('}') + self.inside_citation_reference_label = False + else: + self.out.append(']}') + + def visit_classifier(self, node): + self.out.append( '(\\textbf{' ) + + def depart_classifier(self, node): + self.out.append( '})\n' ) + + def visit_colspec(self, node): + self.active_table.visit_colspec(node) + + def depart_colspec(self, node): + pass + + def visit_comment(self, node): + # Precede every line with a comment sign, wrap in newlines + self.out.append('\n%% %s\n' % node.astext().replace('\n', '\n% ')) + raise nodes.SkipNode + + def depart_comment(self, node): + pass + + def visit_compound(self, node): + pass + + def depart_compound(self, node): + pass + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact') + + def depart_contact(self, node): + self.depart_docinfo_item(node) + + def visit_container(self, node): + pass + + def depart_container(self, node): + pass + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def depart_copyright(self, node): + self.depart_docinfo_item(node) + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def depart_date(self, node): + self.depart_docinfo_item(node) + + def visit_decoration(self, node): + # header and footer + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + pass + + def depart_definition(self, node): + self.out.append('\n') + + def visit_definition_list(self, node): + self.out.append( '%\n\\begin{description}\n' ) + + def depart_definition_list(self, node): + self.out.append( '\\end{description}\n' ) + + def visit_definition_list_item(self, node): + pass + + def depart_definition_list_item(self, node): + pass + + def visit_description(self, node): + self.out.append(' ') + + def depart_description(self, node): + pass + + def visit_docinfo(self, node): + self.push_output_collector(self.docinfo) + + def depart_docinfo(self, node): + self.pop_output_collector() + # Some itmes (e.g. author) end up at other places + if self.docinfo: + # tabularx: automatic width of columns, no page breaks allowed. + self.requirements['tabularx'] = r'\usepackage{tabularx}' + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['docinfo'] = PreambleCmds.docinfo + # + self.docinfo.insert(0, '\n% Docinfo\n' + '\\begin{center}\n' + '\\begin{tabularx}{\\DUdocinfowidth}{lX}\n') + self.docinfo.append('\\end{tabularx}\n' + '\\end{center}\n') + + def visit_docinfo_item(self, node, name): + if name == 'author': + self.pdfauthor.append(self.attval(node.astext())) + if self.use_latex_docinfo: + if name in ('author', 'organization', 'contact', 'address'): + # We attach these to the last author. If any of them precedes + # the first author, put them in a separate "author" group + # (in lack of better semantics). + if name == 'author' or not self.author_stack: + self.author_stack.append([]) + if name == 'address': # newlines are meaningful + self.insert_newline = True + text = self.encode(node.astext()) + self.insert_newline = False + else: + text = self.attval(node.astext()) + self.author_stack[-1].append(text) + raise nodes.SkipNode + elif name == 'date': + self.date.append(self.attval(node.astext())) + raise nodes.SkipNode + self.out.append('\\textbf{%s}: &\n\t' % self.language_label(name)) + if name == 'address': + self.insert_newline = 1 + self.out.append('{\\raggedright\n') + self.context.append(' } \\\\\n') + else: + self.context.append(' \\\\\n') + + def depart_docinfo_item(self, node): + self.out.append(self.context.pop()) + # for address we did set insert_newline + self.insert_newline = False + + def visit_doctest_block(self, node): + self.visit_literal_block(node) + + def depart_doctest_block(self, node): + self.depart_literal_block(node) + + def visit_document(self, node): + # titled document? + if (self.use_latex_docinfo or len(node) and + isinstance(node[0], nodes.title)): + self.title_labels += self.ids_to_labels(node, set_anchor=False) + + def depart_document(self, node): + # Complete header with information gained from walkabout + # * language setup + if (self.babel.otherlanguages or + self.babel.language not in ('', 'english')): + self.requirements['babel'] = self.babel() + # * conditional requirements (before style sheet) + self.requirements = self.requirements.sortedvalues() + # * coditional fallback definitions (after style sheet) + self.fallbacks = self.fallbacks.sortedvalues() + # * PDF properties + self.pdfsetup.append(PreambleCmds.linking % self.hyperref_options) + if self.pdfauthor: + authors = self.author_separator.join(self.pdfauthor) + self.pdfinfo.append(' pdfauthor={%s}' % authors) + if self.pdfinfo: + self.pdfsetup += [r'\hypersetup{'] + self.pdfinfo + ['}'] + # Complete body + # * document title (with "use_latex_docinfo" also + # 'author', 'organization', 'contact', 'address' and 'date') + if self.title or ( + self.use_latex_docinfo and (self.author_stack or self.date)): + # with the default template, titledata is written to the preamble + self.titledata.append('%%% Title Data') + # \title (empty \title prevents error with \maketitle) + if self.title: + self.title.insert(0, '\phantomsection%\n ') + title = [''.join(self.title)] + self.title_labels + if self.subtitle: + title += [r'\\ % subtitle', + r'\DUdocumentsubtitle{%s}' % ''.join(self.subtitle) + ] + self.subtitle_labels + self.titledata.append(r'\title{%s}' % '%\n '.join(title)) + # \author (empty \author prevents warning with \maketitle) + authors = ['\\\\\n'.join(author_entry) + for author_entry in self.author_stack] + self.titledata.append(r'\author{%s}' % + ' \\and\n'.join(authors)) + # \date (empty \date prevents defaulting to \today) + self.titledata.append(r'\date{%s}' % ', '.join(self.date)) + # \maketitle in the body formats title with LaTeX + self.body_pre_docinfo.append('\\maketitle\n') + + # * bibliography + # TODO insertion point of bibliography should be configurable. + if self._use_latex_citations and len(self._bibitems)>0: + if not self.bibtex: + widest_label = '' + for bi in self._bibitems: + if len(widest_label) self._max_enumeration_counters: + self._max_enumeration_counters = len(self._enumeration_counters) + self.out.append('\\newcounter{%s}\n' % counter_name) + else: + self.out.append('\\setcounter{%s}{0}\n' % counter_name) + + self.out.append('\\begin{list}{%s\\%s{%s}%s}\n' % + (enum_prefix,enum_type,counter_name,enum_suffix)) + self.out.append('{\n') + self.out.append('\\usecounter{%s}\n' % counter_name) + # set start after usecounter, because it initializes to zero. + if 'start' in node: + self.out.append('\\addtocounter{%s}{%d}\n' % + (counter_name,node['start']-1)) + ## set rightmargin equal to leftmargin + self.out.append('\\setlength{\\rightmargin}{\\leftmargin}\n') + self.out.append('}\n') + + def depart_enumerated_list(self, node): + self.out.append('\\end{list}\n') + self._enumeration_counters.pop() + + def visit_field(self, node): + # real output is done in siblings: _argument, _body, _name + pass + + def depart_field(self, node): + self.out.append('\n') + ##self.out.append('%[depart_field]\n') + + def visit_field_argument(self, node): + self.out.append('%[visit_field_argument]\n') + + def depart_field_argument(self, node): + self.out.append('%[depart_field_argument]\n') + + def visit_field_body(self, node): + pass + + def depart_field_body(self, node): + if self.out is self.docinfo: + self.out.append(r'\\') + + def visit_field_list(self, node): + if self.out is not self.docinfo: + self.fallbacks['fieldlist'] = PreambleCmds.fieldlist + self.out.append('%\n\\begin{DUfieldlist}\n') + + def depart_field_list(self, node): + if self.out is not self.docinfo: + self.out.append('\\end{DUfieldlist}\n') + + def visit_field_name(self, node): + if self.out is self.docinfo: + self.out.append('\\textbf{') + else: + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + self.out.append('\\item[{') + + def depart_field_name(self, node): + if self.out is self.docinfo: + self.out.append('}: &') + else: + self.out.append(':}]') + + def visit_figure(self, node): + self.requirements['float_settings'] = PreambleCmds.float_settings + # The 'align' attribute sets the "outer alignment", + # for "inner alignment" use LaTeX default alignment (similar to HTML) + alignment = node.attributes.get('align', 'center') + if alignment != 'center': + # The LaTeX "figure" environment always uses the full textwidth, + # so "outer alignment" is ignored. Just write a comment. + # TODO: use the wrapfigure environment? + self.out.append('\n\\begin{figure} %% align = "%s"\n' % alignment) + else: + self.out.append('\n\\begin{figure}\n') + if node.get('ids'): + self.out += self.ids_to_labels(node) + ['\n'] + + def depart_figure(self, node): + self.out.append('\\end{figure}\n') + + def visit_footer(self, node): + self.push_output_collector([]) + self.out.append(r'\newcommand{\DUfooter}{') + + def depart_footer(self, node): + self.out.append('}') + self.requirements['~footer'] = ''.join(self.out) + self.pop_output_collector() + + def visit_footnote(self, node): + try: + backref = node['backrefs'][0] + except IndexError: + backref = node['ids'][0] # no backref, use self-ref instead + if self.settings.figure_footnotes: + self.requirements['~fnt_floats'] = PreambleCmds.footnote_floats + self.out.append('\\begin{figure}[b]') + self.append_hypertargets(node) + if node.get('id') == node.get('name'): # explicite label + self.out += self.ids_to_labels(node) + elif self.docutils_footnotes: + self.fallbacks['footnotes'] = PreambleCmds.footnotes + num,text = node.astext().split(None,1) + if self.settings.footnote_references == 'brackets': + num = '[%s]' % num + self.out.append('%%\n\\DUfootnotetext{%s}{%s}{%s}{' % + (node['ids'][0], backref, self.encode(num))) + if node['ids'] == node['names']: + self.out += self.ids_to_labels(node) + # mask newline to prevent spurious whitespace: + self.out.append('%') + ## else: # TODO: "real" LaTeX \footnote{}s + + def depart_footnote(self, node): + if self.figure_footnotes: + self.out.append('\\end{figure}\n') + else: + self.out.append('}\n') + + def visit_footnote_reference(self, node): + href = '' + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + # if not self.docutils_footnotes: + # TODO: insert footnote content at (or near) this place + # print "footnote-ref to", node['refid'] + # footnotes = (self.document.footnotes + + # self.document.autofootnotes + + # self.document.symbol_footnotes) + # for footnote in footnotes: + # # print footnote['ids'] + # if node.get('refid', '') in footnote['ids']: + # print 'matches', footnote['ids'] + format = self.settings.footnote_references + if format == 'brackets': + self.append_hypertargets(node) + self.out.append('\\hyperlink{%s}{[' % href) + self.context.append(']}') + else: + self.fallbacks['footnotes'] = PreambleCmds.footnotes + self.out.append(r'\DUfootnotemark{%s}{%s}{' % + (node['ids'][0], href)) + self.context.append('}') + + def depart_footnote_reference(self, node): + self.out.append(self.context.pop()) + + # footnote/citation label + def label_delim(self, node, bracket, superscript): + if isinstance(node.parent, nodes.footnote): + if not self.figure_footnotes: + raise nodes.SkipNode + if self.settings.footnote_references == 'brackets': + self.out.append(bracket) + else: + self.out.append(superscript) + else: + assert isinstance(node.parent, nodes.citation) + if not self._use_latex_citations: + self.out.append(bracket) + + def visit_label(self, node): + """footnote or citation label: in brackets or as superscript""" + self.label_delim(node, '[', '\\textsuperscript{') + + def depart_label(self, node): + self.label_delim(node, ']', '}') + + # elements generated by the framework e.g. section numbers. + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + self.push_output_collector([]) + self.out.append(r'\newcommand{\DUheader}{') + + def depart_header(self, node): + self.out.append('}') + self.requirements['~header'] = ''.join(self.out) + self.pop_output_collector() + + def to_latex_length(self, length_str, pxunit=None): + """Convert `length_str` with rst lenght to LaTeX length + """ + if pxunit is not None: + sys.stderr.write('deprecation warning: LaTeXTranslator.to_latex_length()' + ' option `pxunit` will be removed.') + match = re.match('(\d*\.?\d*)\s*(\S*)', length_str) + if not match: + return length_str + value, unit = match.groups()[:2] + # no unit or "DTP" points (called 'bp' in TeX): + if unit in ('', 'pt'): + length_str = '%sbp' % value + # percentage: relate to current line width + elif unit == '%': + length_str = '%.3f\\linewidth' % (float(value)/100.0) + elif self.is_xetex and unit == 'px': + # XeTeX does not know the length unit px. + # Use \pdfpxdimen, the macro to set the value of 1 px in pdftex. + # This way, configuring works the same for pdftex and xetex. + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['px'] = '\n\\DUprovidelength{\\pdfpxdimen}{1bp}\n' + length_str = r'%s\pdfpxdimen' % value + return length_str + + def visit_image(self, node): + self.requirements['graphicx'] = self.graphicx_package + attrs = node.attributes + # Convert image URI to a local file path + imagepath = urllib.request.url2pathname(attrs['uri']).replace('\\', '/') + # alignment defaults: + if not 'align' in attrs: + # Set default align of image in a figure to 'center' + if isinstance(node.parent, nodes.figure): + attrs['align'] = 'center' + # query 'align-*' class argument + for cls in node['classes']: + if cls.startswith('align-'): + attrs['align'] = cls.split('-')[1] + # pre- and postfix (prefix inserted in reverse order) + pre = [] + post = [] + include_graphics_options = [] + align_codes = { + # inline images: by default latex aligns the bottom. + 'bottom': ('', ''), + 'middle': (r'\raisebox{-0.5\height}{', '}'), + 'top': (r'\raisebox{-\height}{', '}'), + # block level images: + 'center': (r'\noindent\makebox[\textwidth][c]{', '}'), + 'left': (r'\noindent{', r'\hfill}'), + 'right': (r'\noindent{\hfill', '}'),} + if 'align' in attrs: + # TODO: warn or ignore non-applicable alignment settings? + try: + align_code = align_codes[attrs['align']] + pre.append(align_code[0]) + post.append(align_code[1]) + except KeyError: + pass # TODO: warn? + if 'height' in attrs: + include_graphics_options.append('height=%s' % + self.to_latex_length(attrs['height'])) + if 'scale' in attrs: + include_graphics_options.append('scale=%f' % + (attrs['scale'] / 100.0)) + if 'width' in attrs: + include_graphics_options.append('width=%s' % + self.to_latex_length(attrs['width'])) + if not (self.is_inline(node) or + isinstance(node.parent, nodes.figure)): + pre.append('\n') + post.append('\n') + pre.reverse() + self.out.extend(pre) + options = '' + if include_graphics_options: + options = '[%s]' % (','.join(include_graphics_options)) + self.out.append('\\includegraphics%s{%s}' % (options, imagepath)) + self.out.extend(post) + + def depart_image(self, node): + if node.get('ids'): + self.out += self.ids_to_labels(node) + ['\n'] + + def visit_inline(self, node): # , i.e. custom roles + self.context.append('}' * len(node['classes'])) + for cls in node['classes']: + if cls == 'align-center': + self.fallbacks['align-center'] = PreambleCmds.align_center + if cls.startswith('language-'): + language = self.babel.language_name(cls[9:]) + if language: + self.babel.otherlanguages[language] = True + self.out.append(r'\foreignlanguage{%s}{' % language) + else: + self.fallbacks['inline'] = PreambleCmds.inline + self.out.append(r'\DUrole{%s}{' % cls) + + def depart_inline(self, node): + self.out.append(self.context.pop()) + + def visit_interpreted(self, node): + # @@@ Incomplete, pending a proper implementation on the + # Parser/Reader end. + self.visit_literal(node) + + def depart_interpreted(self, node): + self.depart_literal(node) + + def visit_legend(self, node): + self.fallbacks['legend'] = PreambleCmds.legend + self.out.append('\\begin{DUlegend}') + + def depart_legend(self, node): + self.out.append('\\end{DUlegend}\n') + + def visit_line(self, node): + self.out.append('\item[] ') + + def depart_line(self, node): + self.out.append('\n') + + def visit_line_block(self, node): + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['lineblock'] = PreambleCmds.lineblock + if isinstance(node.parent, nodes.line_block): + self.out.append('\\item[]\n' + '\\begin{DUlineblock}{\\DUlineblockindent}\n') + else: + self.out.append('\n\\begin{DUlineblock}{0em}\n') + if node['classes']: + self.visit_inline(node) + self.out.append('\n') + + def depart_line_block(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('\n') + self.out.append('\\end{DUlineblock}\n') + + def visit_list_item(self, node): + self.out.append('\n\\item ') + + def depart_list_item(self, node): + pass + + def visit_literal(self, node): + self.literal = True + if 'code' in node['classes'] and ( + self.settings.syntax_highlight != 'none'): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['code'] = PreambleCmds.highlight_rules + self.out.append('\\texttt{') + if node['classes']: + self.visit_inline(node) + + def depart_literal(self, node): + self.literal = False + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + # Literal blocks are used for '::'-prefixed literal-indented + # blocks of text, where the inline markup is not recognized, + # but are also the product of the "parsed-literal" directive, + # where the markup is respected. + # + # In both cases, we want to use a typewriter/monospaced typeface. + # For "real" literal-blocks, we can use \verbatim, while for all + # the others we must use \mbox or \alltt. + # + # We can distinguish between the two kinds by the number of + # siblings that compose this node: if it is composed by a + # single element, it's either + # * a real one, + # * a parsed-literal that does not contain any markup, or + # * a parsed-literal containing just one markup construct. + def is_plaintext(self, node): + """Check whether a node can be typeset verbatim""" + return (len(node) == 1) and isinstance(node[0], nodes.Text) + + def visit_literal_block(self, node): + """Render a literal block.""" + # environments and packages to typeset literal blocks + packages = {'listing': r'\usepackage{moreverb}', + 'lstlisting': r'\usepackage{listings}', + 'Verbatim': r'\usepackage{fancyvrb}', + # 'verbatim': '', + 'verbatimtab': r'\usepackage{moreverb}'} + + if not self.active_table.is_open(): + # no quote inside tables, to avoid vertical space between + # table border and literal block. + # BUG: fails if normal text precedes the literal block. + self.out.append('%\n\\begin{quote}') + self.context.append('\n\\end{quote}\n') + else: + self.out.append('\n') + self.context.append('\n') + if self.literal_block_env != '' and self.is_plaintext(node): + self.requirements['literal_block'] = packages.get( + self.literal_block_env, '') + self.verbatim = True + self.out.append('\\begin{%s}%s\n' % (self.literal_block_env, + self.literal_block_options)) + else: + self.literal = True + self.insert_newline = True + self.insert_non_breaking_blanks = True + if 'code' in node['classes'] and ( + self.settings.syntax_highlight != 'none'): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['code'] = PreambleCmds.highlight_rules + self.out.append('{\\ttfamily \\raggedright \\noindent\n') + + def depart_literal_block(self, node): + if self.verbatim: + self.out.append('\n\\end{%s}\n' % self.literal_block_env) + self.verbatim = False + else: + self.out.append('\n}') + self.insert_non_breaking_blanks = False + self.insert_newline = False + self.literal = False + self.out.append(self.context.pop()) + + ## def visit_meta(self, node): + ## self.out.append('[visit_meta]\n') + # TODO: set keywords for pdf? + # But: + # The reStructuredText "meta" directive creates a "pending" node, + # which contains knowledge that the embedded "meta" node can only + # be handled by HTML-compatible writers. The "pending" node is + # resolved by the docutils.transforms.components.Filter transform, + # which checks that the calling writer supports HTML; if it doesn't, + # the "pending" node (and enclosed "meta" node) is removed from the + # document. + # --- docutils/docs/peps/pep-0258.html#transformer + + ## def depart_meta(self, node): + ## self.out.append('[depart_meta]\n') + + def visit_math(self, node, math_env='$'): + """math role""" + if node['classes']: + self.visit_inline(node) + self.requirements['amsmath'] = r'\usepackage{amsmath}' + math_code = node.astext().translate(unichar2tex.uni2tex_table) + if node.get('ids'): + math_code = '\n'.join([math_code] + self.ids_to_labels(node)) + if math_env == '$': + wrapper = '$%s$' + else: + wrapper = '\n'.join(['%%', + r'\begin{%s}' % math_env, + '%s', + r'\end{%s}' % math_env]) + # print repr(wrapper), repr(math_code) + self.out.append(wrapper % math_code) + if node['classes']: + self.depart_inline(node) + # Content already processed: + raise nodes.SkipNode + + def depart_math(self, node): + pass # never reached + + def visit_math_block(self, node): + math_env = pick_math_environment(node.astext()) + self.visit_math(node, math_env=math_env) + + def depart_math_block(self, node): + pass # never reached + + def visit_option(self, node): + if self.context[-1]: + # this is not the first option + self.out.append(', ') + + def depart_option(self, node): + # flag that the first option is done. + self.context[-1] += 1 + + def visit_option_argument(self, node): + """Append the delimiter betweeen an option and its argument to body.""" + self.out.append(node.get('delimiter', ' ')) + + def depart_option_argument(self, node): + pass + + def visit_option_group(self, node): + self.out.append('\n\\item[') + # flag for first option + self.context.append(0) + + def depart_option_group(self, node): + self.context.pop() # the flag + self.out.append('] ') + + def visit_option_list(self, node): + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['optionlist'] = PreambleCmds.optionlist + self.out.append('%\n\\begin{DUoptionlist}\n') + + def depart_option_list(self, node): + self.out.append('\n\\end{DUoptionlist}\n') + + def visit_option_list_item(self, node): + pass + + def depart_option_list_item(self, node): + pass + + def visit_option_string(self, node): + ##self.out.append(self.starttag(node, 'span', '', CLASS='option')) + pass + + def depart_option_string(self, node): + ##self.out.append('') + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + self.depart_docinfo_item(node) + + def visit_paragraph(self, node): + # insert blank line, if the paragraph is not first in a list item + # nor follows a non-paragraph node in a compound + index = node.parent.index(node) + if (index == 0 and (isinstance(node.parent, nodes.list_item) or + isinstance(node.parent, nodes.description))): + pass + elif (index > 0 and isinstance(node.parent, nodes.compound) and + not isinstance(node.parent[index - 1], nodes.paragraph) and + not isinstance(node.parent[index - 1], nodes.compound)): + pass + else: + self.out.append('\n') + if node.get('ids'): + self.out += self.ids_to_labels(node) + ['\n'] + if node['classes']: + self.visit_inline(node) + + def depart_paragraph(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('\n') + + def visit_problematic(self, node): + self.requirements['color'] = PreambleCmds.color + self.out.append('%\n') + self.append_hypertargets(node) + self.out.append(r'\hyperlink{%s}{\textbf{\color{red}' % node['refid']) + + def depart_problematic(self, node): + self.out.append('}}') + + def visit_raw(self, node): + if not 'latex' in node.get('format', '').split(): + raise nodes.SkipNode + if not self.is_inline(node): + self.out.append('\n') + if node['classes']: + self.visit_inline(node) + # append "as-is" skipping any LaTeX-encoding + self.verbatim = True + + def depart_raw(self, node): + self.verbatim = False + if node['classes']: + self.depart_inline(node) + if not self.is_inline(node): + self.out.append('\n') + + def has_unbalanced_braces(self, string): + """Test whether there are unmatched '{' or '}' characters.""" + level = 0 + for ch in string: + if ch == '{': + level += 1 + if ch == '}': + level -= 1 + if level < 0: + return True + return level != 0 + + def visit_reference(self, node): + # We need to escape #, \, and % if we use the URL in a command. + special_chars = {ord('#'): r'\#', + ord('%'): r'\%', + ord('\\'): r'\\', + } + # external reference (URL) + if 'refuri' in node: + href = str(node['refuri']).translate(special_chars) + # problematic chars double caret and unbalanced braces: + if href.find('^^') != -1 or self.has_unbalanced_braces(href): + self.error( + 'External link "%s" not supported by LaTeX.\n' + ' (Must not contain "^^" or unbalanced braces.)' % href) + if node['refuri'] == node.astext(): + self.out.append(r'\url{%s}' % href) + raise nodes.SkipNode + self.out.append(r'\href{%s}{' % href) + return + # internal reference + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + else: + raise AssertionError('Unknown reference.') + if not self.is_inline(node): + self.out.append('\n') + self.out.append('\\hyperref[%s]{' % href) + if self._reference_label: + self.out.append('\\%s{%s}}' % + (self._reference_label, href.replace('#', ''))) + raise nodes.SkipNode + + def depart_reference(self, node): + self.out.append('}') + if not self.is_inline(node): + self.out.append('\n') + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision') + + def depart_revision(self, node): + self.depart_docinfo_item(node) + + def visit_section(self, node): + self.section_level += 1 + # Initialize counter for potential subsections: + self._section_number.append(0) + # Counter for this section's level (initialized by parent section): + self._section_number[self.section_level - 1] += 1 + + def depart_section(self, node): + # Remove counter for potential subsections: + self._section_number.pop() + self.section_level -= 1 + + def visit_sidebar(self, node): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['sidebar'] = PreambleCmds.sidebar + self.out.append('\n\\DUsidebar{\n') + + def depart_sidebar(self, node): + self.out.append('}\n') + + attribution_formats = {'dash': ('—', ''), # EM DASH + 'parentheses': ('(', ')'), + 'parens': ('(', ')'), + 'none': ('', '')} + + def visit_attribution(self, node): + prefix, suffix = self.attribution_formats[self.settings.attribution] + self.out.append('\\nopagebreak\n\n\\raggedleft ') + self.out.append(prefix) + self.context.append(suffix) + + def depart_attribution(self, node): + self.out.append(self.context.pop() + '\n') + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status') + + def depart_status(self, node): + self.depart_docinfo_item(node) + + def visit_strong(self, node): + self.out.append('\\textbf{') + if node['classes']: + self.visit_inline(node) + + def depart_strong(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_substitution_definition(self, node): + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.unimplemented_visit(node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.document): + self.push_output_collector(self.subtitle) + self.fallbacks['documentsubtitle'] = PreambleCmds.documentsubtitle + self.subtitle_labels += self.ids_to_labels(node, set_anchor=False) + # section subtitle: "starred" (no number, not in ToC) + elif isinstance(node.parent, nodes.section): + self.out.append(r'\%s*{' % + self.d_class.section(self.section_level + 1)) + else: + self.fallbacks['subtitle'] = PreambleCmds.subtitle + self.out.append('\n\\DUsubtitle[%s]{' % node.parent.tagname) + + def depart_subtitle(self, node): + if isinstance(node.parent, nodes.document): + self.pop_output_collector() + else: + self.out.append('}\n') + + def visit_system_message(self, node): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['title'] = PreambleCmds.title + node['classes'] = ['system-message'] + self.visit_admonition(node) + self.out.append('\\DUtitle[system-message]{system-message}\n') + self.append_hypertargets(node) + try: + line = ', line~%s' % node['line'] + except KeyError: + line = '' + self.out.append('\n\n{\color{red}%s/%s} in \\texttt{%s}%s\n' % + (node['type'], node['level'], + self.encode(node['source']), line)) + if len(node['backrefs']) == 1: + self.out.append('\n\\hyperlink{%s}{' % node['backrefs'][0]) + self.context.append('}') + else: + backrefs = ['\\hyperlink{%s}{%d}' % (href, i+1) + for (i, href) in enumerate(node['backrefs'])] + self.context.append('backrefs: ' + ' '.join(backrefs)) + + def depart_system_message(self, node): + self.out.append(self.context.pop()) + self.depart_admonition() + + def visit_table(self, node): + self.requirements['table'] = PreambleCmds.table + if self.active_table.is_open(): + self.table_stack.append(self.active_table) + # nesting longtable does not work (e.g. 2007-04-18) + self.active_table = Table(self,'tabular',self.settings.table_style) + # A longtable moves before \paragraph and \subparagraph + # section titles if it immediately follows them: + if (self.active_table._latex_type == 'longtable' and + isinstance(node.parent, nodes.section) and + node.parent.index(node) == 1 and + self.d_class.section(self.section_level).find('paragraph') != -1): + self.out.append('\\leavevmode') + self.active_table.open() + for cls in node['classes']: + self.active_table.set_table_style(cls) + if self.active_table._table_style == 'booktabs': + self.requirements['booktabs'] = r'\usepackage{booktabs}' + self.push_output_collector([]) + + def depart_table(self, node): + # wrap content in the right environment: + content = self.out + self.pop_output_collector() + self.out.append('\n' + self.active_table.get_opening()) + self.out += content + self.out.append(self.active_table.get_closing() + '\n') + self.active_table.close() + if len(self.table_stack)>0: + self.active_table = self.table_stack.pop() + else: + self.active_table.set_table_style(self.settings.table_style) + # Insert hyperlabel after (long)table, as + # other places (beginning, caption) result in LaTeX errors. + if node.get('ids'): + self.out += self.ids_to_labels(node, set_anchor=False) + ['\n'] + + def visit_target(self, node): + # Skip indirect targets: + if ('refuri' in node # external hyperlink + or 'refid' in node # resolved internal link + or 'refname' in node): # unresolved internal link + ## self.out.append('%% %s\n' % node) # for debugging + return + self.out.append('%\n') + # do we need an anchor (\phantomsection)? + set_anchor = not(isinstance(node.parent, nodes.caption) or + isinstance(node.parent, nodes.title)) + # TODO: where else can/must we omit the \phantomsection? + self.out += self.ids_to_labels(node, set_anchor) + + def depart_target(self, node): + pass + + def visit_tbody(self, node): + # BUG write preamble if not yet done (colspecs not []) + # for tables without heads. + if not self.active_table.get('preamble written'): + self.visit_thead(None) + self.depart_thead(None) + + def depart_tbody(self, node): + pass + + def visit_term(self, node): + """definition list term""" + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + self.out.append('\\item[{') + + def depart_term(self, node): + # \leavevmode results in a line break if the + # term is followed by an item list. + self.out.append('}] \leavevmode ') + + def visit_tgroup(self, node): + #self.out.append(self.starttag(node, 'colgroup')) + #self.context.append('\n') + pass + + def depart_tgroup(self, node): + pass + + _thead_depth = 0 + def thead_depth (self): + return self._thead_depth + + def visit_thead(self, node): + self._thead_depth += 1 + if 1 == self.thead_depth(): + self.out.append('{%s}\n' % self.active_table.get_colspecs()) + self.active_table.set('preamble written',1) + self.out.append(self.active_table.get_caption()) + self.out.extend(self.active_table.visit_thead()) + + def depart_thead(self, node): + if node is not None: + self.out.extend(self.active_table.depart_thead()) + if self.active_table.need_recurse(): + node.walkabout(self) + self._thead_depth -= 1 + + def visit_title(self, node): + """Append section and other titles.""" + # Document title + if node.parent.tagname == 'document': + self.push_output_collector(self.title) + self.context.append('') + self.pdfinfo.append(' pdftitle={%s},' % + self.encode(node.astext())) + # Topic titles (topic, admonition, sidebar) + elif (isinstance(node.parent, nodes.topic) or + isinstance(node.parent, nodes.admonition) or + isinstance(node.parent, nodes.sidebar)): + self.fallbacks['title'] = PreambleCmds.title + classes = ','.join(node.parent['classes']) + if not classes: + classes = node.tagname + self.out.append('\\DUtitle[%s]{' % classes) + self.context.append('}\n') + # Table caption + elif isinstance(node.parent, nodes.table): + self.push_output_collector(self.active_table.caption) + self.context.append('') + # Section title + else: + if hasattr(PreambleCmds, 'secnumdepth'): + self.requirements['secnumdepth'] = PreambleCmds.secnumdepth + section_name = self.d_class.section(self.section_level) + self.out.append('\n\n') + # System messages heading in red: + if ('system-messages' in node.parent['classes']): + self.requirements['color'] = PreambleCmds.color + section_title = self.encode(node.astext()) + self.out.append(r'\%s[%s]{\color{red}' % ( + section_name,section_title)) + else: + self.out.append(r'\%s{' % section_name) + if self.section_level > len(self.d_class.sections): + # section level not supported by LaTeX + self.fallbacks['title'] = PreambleCmds.title + # self.out.append('\\phantomsection%\n ') + # label and ToC entry: + bookmark = [''] + # add sections with unsupported level to toc and pdfbookmarks? + ## if self.section_level > len(self.d_class.sections): + ## section_title = self.encode(node.astext()) + ## bookmark.append(r'\addcontentsline{toc}{%s}{%s}' % + ## (section_name, section_title)) + bookmark += self.ids_to_labels(node.parent, set_anchor=False) + self.context.append('%\n '.join(bookmark) + '%\n}\n') + + # MAYBE postfix paragraph and subparagraph with \leavemode to + # ensure floats stay in the section and text starts on a new line. + + def depart_title(self, node): + self.out.append(self.context.pop()) + if (isinstance(node.parent, nodes.table) or + node.parent.tagname == 'document'): + self.pop_output_collector() + + def minitoc(self, node, title, depth): + """Generate a local table of contents with LaTeX package minitoc""" + section_name = self.d_class.section(self.section_level) + # name-prefix for current section level + minitoc_names = {'part': 'part', 'chapter': 'mini'} + if 'chapter' not in self.d_class.sections: + minitoc_names['section'] = 'sect' + try: + minitoc_name = minitoc_names[section_name] + except KeyError: # minitoc only supports part- and toplevel + self.warn('Skipping local ToC at %s level.\n' % section_name + + ' Feature not supported with option "use-latex-toc"', + base_node=node) + return + # Requirements/Setup + self.requirements['minitoc'] = PreambleCmds.minitoc + self.requirements['minitoc-'+minitoc_name] = (r'\do%stoc' % + minitoc_name) + # depth: (Docutils defaults to unlimited depth) + maxdepth = len(self.d_class.sections) + self.requirements['minitoc-%s-depth' % minitoc_name] = ( + r'\mtcsetdepth{%stoc}{%d}' % (minitoc_name, maxdepth)) + # Process 'depth' argument (!Docutils stores a relative depth while + # minitoc expects an absolute depth!): + offset = {'sect': 1, 'mini': 0, 'part': 0} + if 'chapter' in self.d_class.sections: + offset['part'] = -1 + if depth: + self.out.append('\\setcounter{%stocdepth}{%d}' % + (minitoc_name, depth + offset[minitoc_name])) + # title: + self.out.append('\\mtcsettitle{%stoc}{%s}\n' % (minitoc_name, title)) + # the toc-generating command: + self.out.append('\\%stoc\n' % minitoc_name) + + def visit_topic(self, node): + # Topic nodes can be generic topic, abstract, dedication, or ToC. + # table of contents: + if 'contents' in node['classes']: + self.out.append('\n') + self.out += self.ids_to_labels(node) + # add contents to PDF bookmarks sidebar + if isinstance(node.next_node(), nodes.title): + self.out.append('\n\\pdfbookmark[%d]{%s}{%s}\n' % + (self.section_level+1, + node.next_node().astext(), + node.get('ids', ['contents'])[0] + )) + if self.use_latex_toc: + title = '' + if isinstance(node.next_node(), nodes.title): + title = self.encode(node.pop(0).astext()) + depth = node.get('depth', 0) + if 'local' in node['classes']: + self.minitoc(node, title, depth) + self.context.append('') + return + if depth: + self.out.append('\\setcounter{tocdepth}{%d}\n' % depth) + if title != 'Contents': + self.out.append('\\renewcommand{\\contentsname}{%s}\n' % + title) + self.out.append('\\tableofcontents\n\n') + self.has_latex_toc = True + else: # Docutils generated contents list + # set flag for visit_bullet_list() and visit_title() + self.is_toc_list = True + self.context.append('') + elif ('abstract' in node['classes'] and + self.settings.use_latex_abstract): + self.push_output_collector(self.abstract) + self.out.append('\\begin{abstract}') + self.context.append('\\end{abstract}\n') + if isinstance(node.next_node(), nodes.title): + node.pop(0) # LaTeX provides its own title + else: + self.fallbacks['topic'] = PreambleCmds.topic + # special topics: + if 'abstract' in node['classes']: + self.fallbacks['abstract'] = PreambleCmds.abstract + self.push_output_collector(self.abstract) + if 'dedication' in node['classes']: + self.fallbacks['dedication'] = PreambleCmds.dedication + self.push_output_collector(self.dedication) + self.out.append('\n\\DUtopic[%s]{\n' % ','.join(node['classes'])) + self.context.append('}\n') + + def depart_topic(self, node): + self.out.append(self.context.pop()) + self.is_toc_list = False + if ('abstract' in node['classes'] or + 'dedication' in node['classes']): + self.pop_output_collector() + + def visit_rubric(self, node): + self.fallbacks['rubric'] = PreambleCmds.rubric + self.out.append('\n\\DUrubric{') + self.context.append('}\n') + + def depart_rubric(self, node): + self.out.append(self.context.pop()) + + def visit_transition(self, node): + self.fallbacks['transition'] = PreambleCmds.transition + self.out.append('\n\n') + self.out.append('%' + '_' * 75 + '\n') + self.out.append(r'\DUtransition') + self.out.append('\n\n') + + def depart_transition(self, node): + pass + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version') + + def depart_version(self, node): + self.depart_docinfo_item(node) + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' % + node.__class__.__name__) + +# def unknown_visit(self, node): +# def default_visit(self, node): + +# vim: set ts=4 et ai : diff --git a/python/helpers/py3only/docutils/writers/latex2e/default.tex b/python/helpers/py3only/docutils/writers/latex2e/default.tex new file mode 100644 index 000000000000..69e99fd7b115 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/latex2e/default.tex @@ -0,0 +1,15 @@ +$head_prefix% generated by Docutils +\usepackage{fixltx2e} % LaTeX patches, \textsubscript +\usepackage{cmap} % fix search and cut-and-paste in Acrobat +$requirements +%%% Custom LaTeX preamble +$latex_preamble +%%% User specified packages and stylesheets +$stylesheet +%%% Fallback definitions for Docutils-specific commands +$fallbacks$pdfsetup +$titledata +%%% Body +\begin{document} +$body_pre_docinfo$docinfo$dedication$abstract$body +\end{document} diff --git a/python/helpers/py3only/docutils/writers/latex2e/titlepage.tex b/python/helpers/py3only/docutils/writers/latex2e/titlepage.tex new file mode 100644 index 000000000000..d57deca3dbdf --- /dev/null +++ b/python/helpers/py3only/docutils/writers/latex2e/titlepage.tex @@ -0,0 +1,20 @@ +% generated by Docutils +$head_prefix +\usepackage{fixltx2e} % LaTeX patches, \textsubscript +\usepackage{cmap} % fix search and cut-and-paste in Acrobat +$requirements +%%% Custom LaTeX preamble +$latex_preamble +%%% User specified packages and stylesheets +$stylesheet +%%% Fallback definitions for Docutils-specific commands +$fallbacks$pdfsetup +$titledata +%%% Body +\begin{document} +\begin{titlepage} +$body_pre_docinfo$docinfo$dedication$abstract +\thispagestyle{empty} +\end{titlepage} +$body +\end{document} diff --git a/python/helpers/py3only/docutils/writers/latex2e/xelatex.tex b/python/helpers/py3only/docutils/writers/latex2e/xelatex.tex new file mode 100644 index 000000000000..06eecfdaaf00 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/latex2e/xelatex.tex @@ -0,0 +1,16 @@ +$head_prefix% generated by Docutils +% rubber: set program xelatex +\usepackage[no-sscript]{xltxtra} % loads fixltx2e, metalogo, xunicode, fontspec +% \defaultfontfeatures{Scale=MatchLowercase} +$requirements +%%% Custom LaTeX preamble +$latex_preamble +%%% User specified packages and stylesheets +$stylesheet +%%% Fallback definitions for Docutils-specific commands +$fallbacks$pdfsetup +$titledata +%%% Body +\begin{document} +$body_pre_docinfo$docinfo$dedication$abstract$body +\end{document} diff --git a/python/helpers/py3only/docutils/writers/manpage.py b/python/helpers/py3only/docutils/writers/manpage.py new file mode 100644 index 000000000000..d74a97b49b7c --- /dev/null +++ b/python/helpers/py3only/docutils/writers/manpage.py @@ -0,0 +1,1156 @@ +# -*- coding: utf-8 -*- +# $Id: manpage.py 7628 2013-03-09 10:19:35Z grubert $ +# Author: Engelbert Gruber +# Copyright: This module is put into the public domain. + +""" +Simple man page writer for reStructuredText. + +Man pages (short for "manual pages") contain system documentation on unix-like +systems. The pages are grouped in numbered sections: + + 1 executable programs and shell commands + 2 system calls + 3 library functions + 4 special files + 5 file formats + 6 games + 7 miscellaneous + 8 system administration + +Man pages are written *troff*, a text file formatting system. + +See http://www.tldp.org/HOWTO/Man-Page for a start. + +Man pages have no subsection only parts. +Standard parts + + NAME , + SYNOPSIS , + DESCRIPTION , + OPTIONS , + FILES , + SEE ALSO , + BUGS , + +and + + AUTHOR . + +A unix-like system keeps an index of the DESCRIPTIONs, which is accesable +by the command whatis or apropos. + +""" + +__docformat__ = 'reStructuredText' + +import re + +from docutils import nodes, writers, languages +try: + import roman +except ImportError: + import docutils.utils.roman as roman + +FIELD_LIST_INDENT = 7 +DEFINITION_LIST_INDENT = 7 +OPTION_LIST_INDENT = 7 +BLOCKQOUTE_INDENT = 3.5 +LITERAL_BLOCK_INDENT = 3.5 + +# Define two macros so man/roff can calculate the +# indent/unindent margins by itself +MACRO_DEF = (r""". +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +""") + +class Writer(writers.Writer): + + supported = ('manpage',) + """Formats this writer supports.""" + + output = None + """Final translated form of `document`.""" + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = Translator + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + self.output = visitor.astext() + + +class Table(object): + def __init__(self): + self._rows = [] + self._options = ['center'] + self._tab_char = '\t' + self._coldefs = [] + def new_row(self): + self._rows.append([]) + def append_separator(self, separator): + """Append the separator for table head.""" + self._rows.append([separator]) + def append_cell(self, cell_lines): + """cell_lines is an array of lines""" + start = 0 + if len(cell_lines) > 0 and cell_lines[0] == '.sp\n': + start = 1 + self._rows[-1].append(cell_lines[start:]) + if len(self._coldefs) < len(self._rows[-1]): + self._coldefs.append('l') + def _minimize_cell(self, cell_lines): + """Remove leading and trailing blank and ``.sp`` lines""" + while (cell_lines and cell_lines[0] in ('\n', '.sp\n')): + del cell_lines[0] + while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')): + del cell_lines[-1] + def as_list(self): + text = ['.TS\n'] + text.append(' '.join(self._options) + ';\n') + text.append('|%s|.\n' % ('|'.join(self._coldefs))) + for row in self._rows: + # row = array of cells. cell = array of lines. + text.append('_\n') # line above + text.append('T{\n') + for i in range(len(row)): + cell = row[i] + self._minimize_cell(cell) + text.extend(cell) + if not text[-1].endswith('\n'): + text[-1] += '\n' + if i < len(row)-1: + text.append('T}'+self._tab_char+'T{\n') + else: + text.append('T}\n') + text.append('_\n') + text.append('.TE\n') + return text + +class Translator(nodes.NodeVisitor): + """""" + + words_and_spaces = re.compile(r'\S+| +|\n') + possibly_a_roff_command = re.compile(r'\.\w') + document_start = """Man page generated from reStructuredText.""" + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.settings = settings = document.settings + lcode = settings.language_code + self.language = languages.get_language(lcode, document.reporter) + self.head = [] + self.body = [] + self.foot = [] + self.section_level = 0 + self.context = [] + self.topic_class = '' + self.colspecs = [] + self.compact_p = 1 + self.compact_simple = None + # the list style "*" bullet or "#" numbered + self._list_char = [] + # writing the header .TH and .SH NAME is postboned after + # docinfo. + self._docinfo = { + "title" : "", "title_upper": "", + "subtitle" : "", + "manual_section" : "", "manual_group" : "", + "author" : [], + "date" : "", + "copyright" : "", + "version" : "", + } + self._docinfo_keys = [] # a list to keep the sequence as in source. + self._docinfo_names = {} # to get name from text not normalized. + self._in_docinfo = None + self._active_table = None + self._in_literal = False + self.header_written = 0 + self._line_block = 0 + self.authors = [] + self.section_level = 0 + self._indent = [0] + # central definition of simple processing rules + # what to output on : visit, depart + # Do not use paragraph requests ``.PP`` because these set indentation. + # use ``.sp``. Remove superfluous ``.sp`` in ``astext``. + # + # Fonts are put on a stack, the top one is used. + # ``.ft P`` or ``\\fP`` pop from stack. + # ``B`` bold, ``I`` italic, ``R`` roman should be available. + # Hopefully ``C`` courier too. + self.defs = { + 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'), + 'definition_list_item' : ('.TP', ''), + 'field_name' : ('.TP\n.B ', '\n'), + 'literal' : ('\\fB', '\\fP'), + 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'), + + 'option_list_item' : ('.TP\n', ''), + + 'reference' : (r'\fI\%', r'\fP'), + 'emphasis': ('\\fI', '\\fP'), + 'strong' : ('\\fB', '\\fP'), + 'term' : ('\n.B ', '\n'), + 'title_reference' : ('\\fI', '\\fP'), + + 'topic-title' : ('.SS ',), + 'sidebar-title' : ('.SS ',), + + 'problematic' : ('\n.nf\n', '\n.fi\n'), + } + # NOTE do not specify the newline before a dot-command, but ensure + # it is there. + + def comment_begin(self, text): + """Return commented version of the passed text WITHOUT end of + line/comment.""" + prefix = '.\\" ' + out_text = ''.join( + [(prefix + in_line + '\n') + for in_line in text.split('\n')]) + return out_text + + def comment(self, text): + """Return commented version of the passed text.""" + return self.comment_begin(text)+'.\n' + + def ensure_eol(self): + """Ensure the last line in body is terminated by new line.""" + if len(self.body) > 0 and self.body[-1][-1] != '\n': + self.body.append('\n') + + def astext(self): + """Return the final formatted document as a string.""" + if not self.header_written: + # ensure we get a ".TH" as viewers require it. + self.append_header() + # filter body + for i in range(len(self.body)-1, 0, -1): + # remove superfluous vertical gaps. + if self.body[i] == '.sp\n': + if self.body[i - 1][:4] in ('.BI ','.IP '): + self.body[i] = '.\n' + elif (self.body[i - 1][:3] == '.B ' and + self.body[i - 2][:4] == '.TP\n'): + self.body[i] = '.\n' + elif (self.body[i - 1] == '\n' and + not self.possibly_a_roff_command.match(self.body[i - 2]) and + (self.body[i - 3][:7] == '.TP\n.B ' + or self.body[i - 3][:4] == '\n.B ') + ): + self.body[i] = '.\n' + return ''.join(self.head + self.body + self.foot) + + def deunicode(self, text): + text = text.replace('\xa0', '\\ ') + text = text.replace('\u2020', '\\(dg') + return text + + def visit_Text(self, node): + text = node.astext() + text = text.replace('\\','\\e') + replace_pairs = [ + ('-', r'\-'), + ('\'', r'\(aq'), + ('´', r'\''), + ('`', r'\(ga'), + ] + for (in_char, out_markup) in replace_pairs: + text = text.replace(in_char, out_markup) + # unicode + text = self.deunicode(text) + # prevent interpretation of "." at line start + if text.startswith('.'): + text = '\\&' + text + if self._in_literal: + text = text.replace('\n.', '\n\\&.') + self.body.append(text) + + def depart_Text(self, node): + pass + + def list_start(self, node): + class enum_char(object): + enum_style = { + 'bullet' : '\\(bu', + 'emdash' : '\\(em', + } + + def __init__(self, style): + self._style = style + if 'start' in node: + self._cnt = node['start'] - 1 + else: + self._cnt = 0 + self._indent = 2 + if style == 'arabic': + # indentation depends on number of childrens + # and start value. + self._indent = len(str(len(node.children))) + self._indent += len(str(self._cnt)) + 1 + elif style == 'loweralpha': + self._cnt += ord('a') - 1 + self._indent = 3 + elif style == 'upperalpha': + self._cnt += ord('A') - 1 + self._indent = 3 + elif style.endswith('roman'): + self._indent = 5 + + def __next__(self): + if self._style == 'bullet': + return self.enum_style[self._style] + elif self._style == 'emdash': + return self.enum_style[self._style] + self._cnt += 1 + # TODO add prefix postfix + if self._style == 'arabic': + return "%d." % self._cnt + elif self._style in ('loweralpha', 'upperalpha'): + return "%c." % self._cnt + elif self._style.endswith('roman'): + res = roman.toRoman(self._cnt) + '.' + if self._style.startswith('upper'): + return res.upper() + return res.lower() + else: + return "%d." % self._cnt + def get_width(self): + return self._indent + def __repr__(self): + return 'enum_style-%s' % list(self._style) + + if 'enumtype' in node: + self._list_char.append(enum_char(node['enumtype'])) + else: + self._list_char.append(enum_char('bullet')) + if len(self._list_char) > 1: + # indent nested lists + self.indent(self._list_char[-2].get_width()) + else: + self.indent(self._list_char[-1].get_width()) + + def list_end(self): + self.dedent() + self._list_char.pop() + + def header(self): + tmpl = (".TH %(title_upper)s %(manual_section)s" + " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" + ".SH NAME\n" + "%(title)s \- %(subtitle)s\n") + return tmpl % self._docinfo + + def append_header(self): + """append header with .TH and .SH NAME""" + # NOTE before everything + # .TH title_upper section date source manual + if self.header_written: + return + self.head.append(self.header()) + self.head.append(MACRO_DEF) + self.header_written = 1 + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address') + + def depart_address(self, node): + pass + + def visit_admonition(self, node, name=None): + # + # Make admonitions a simple block quote + # with a strong heading + # + # Using .IP/.RE doesn't preserve indentation + # when admonitions contain bullets, literal, + # and/or block quotes. + # + if name: + # .. admonition:: has no name + self.body.append('.sp\n') + name = '%s%s:%s\n' % ( + self.defs['strong'][0], + self.language.labels.get(name, name).upper(), + self.defs['strong'][1], + ) + self.body.append(name) + self.visit_block_quote(node) + + def depart_admonition(self, node): + self.depart_block_quote(node) + + def visit_attention(self, node): + self.visit_admonition(node, 'attention') + + depart_attention = depart_admonition + + def visit_docinfo_item(self, node, name): + if name == 'author': + self._docinfo[name].append(node.astext()) + else: + self._docinfo[name] = node.astext() + self._docinfo_keys.append(name) + raise nodes.SkipNode + + def depart_docinfo_item(self, node): + pass + + def visit_author(self, node): + self.visit_docinfo_item(node, 'author') + + depart_author = depart_docinfo_item + + def visit_authors(self, node): + # _author is called anyway. + pass + + def depart_authors(self, node): + pass + + def visit_block_quote(self, node): + # BUG/HACK: indent alway uses the _last_ indention, + # thus we need two of them. + self.indent(BLOCKQOUTE_INDENT) + self.indent(0) + + def depart_block_quote(self, node): + self.dedent() + self.dedent() + + def visit_bullet_list(self, node): + self.list_start(node) + + def depart_bullet_list(self, node): + self.list_end() + + def visit_caption(self, node): + pass + + def depart_caption(self, node): + pass + + def visit_caution(self, node): + self.visit_admonition(node, 'caution') + + depart_caution = depart_admonition + + def visit_citation(self, node): + num, text = node.astext().split(None, 1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % num) + + def depart_citation(self, node): + pass + + def visit_citation_reference(self, node): + self.body.append('['+node.astext()+']') + raise nodes.SkipNode + + def visit_classifier(self, node): + pass + + def depart_classifier(self, node): + pass + + def visit_colspec(self, node): + self.colspecs.append(node) + + def depart_colspec(self, node): + pass + + def write_colspecs(self): + self.body.append("%s.\n" % ('L '*len(self.colspecs))) + + def visit_comment(self, node, + sub=re.compile('-(?=-)').sub): + self.body.append(self.comment(node.astext())) + raise nodes.SkipNode + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact') + + depart_contact = depart_docinfo_item + + def visit_container(self, node): + pass + + def depart_container(self, node): + pass + + def visit_compound(self, node): + pass + + def depart_compound(self, node): + pass + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def visit_danger(self, node): + self.visit_admonition(node, 'danger') + + depart_danger = depart_admonition + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + pass + + def depart_definition(self, node): + pass + + def visit_definition_list(self, node): + self.indent(DEFINITION_LIST_INDENT) + + def depart_definition_list(self, node): + self.dedent() + + def visit_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][0]) + + def depart_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][1]) + + def visit_description(self, node): + pass + + def depart_description(self, node): + pass + + def visit_docinfo(self, node): + self._in_docinfo = 1 + + def depart_docinfo(self, node): + self._in_docinfo = None + # NOTE nothing should be written before this + self.append_header() + + def visit_doctest_block(self, node): + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_doctest_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + + def visit_document(self, node): + # no blank line between comment and header. + self.head.append(self.comment(self.document_start).rstrip()+'\n') + # writing header is postboned + self.header_written = 0 + + def depart_document(self, node): + if self._docinfo['author']: + self.body.append('.SH AUTHOR\n%s\n' + % ', '.join(self._docinfo['author'])) + skip = ('author', 'copyright', 'date', + 'manual_group', 'manual_section', + 'subtitle', + 'title', 'title_upper', 'version') + for name in self._docinfo_keys: + if name == 'address': + self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % ( + self.language.labels.get(name, name), + self.defs['indent'][0] % 0, + self.defs['indent'][0] % BLOCKQOUTE_INDENT, + self._docinfo[name], + self.defs['indent'][1], + self.defs['indent'][1])) + elif not name in skip: + if name in self._docinfo_names: + label = self._docinfo_names[name] + else: + label = self.language.labels.get(name, name) + self.body.append("\n%s: %s\n" % (label, self._docinfo[name])) + if self._docinfo['copyright']: + self.body.append('.SH COPYRIGHT\n%s\n' + % self._docinfo['copyright']) + self.body.append(self.comment( + 'Generated by docutils manpage writer.')) + + def visit_emphasis(self, node): + self.body.append(self.defs['emphasis'][0]) + + def depart_emphasis(self, node): + self.body.append(self.defs['emphasis'][1]) + + def visit_entry(self, node): + # a cell in a table row + if 'morerows' in node: + self.document.reporter.warning('"table row spanning" not supported', + base_node=node) + if 'morecols' in node: + self.document.reporter.warning( + '"table cell spanning" not supported', base_node=node) + self.context.append(len(self.body)) + + def depart_entry(self, node): + start = self.context.pop() + self._active_table.append_cell(self.body[start:]) + del self.body[start:] + + def visit_enumerated_list(self, node): + self.list_start(node) + + def depart_enumerated_list(self, node): + self.list_end() + + def visit_error(self, node): + self.visit_admonition(node, 'error') + + depart_error = depart_admonition + + def visit_field(self, node): + pass + + def depart_field(self, node): + pass + + def visit_field_body(self, node): + if self._in_docinfo: + name_normalized = self._field_name.lower().replace(" ","_") + self._docinfo_names[name_normalized] = self._field_name + self.visit_docinfo_item(node, name_normalized) + raise nodes.SkipNode + + def depart_field_body(self, node): + pass + + def visit_field_list(self, node): + self.indent(FIELD_LIST_INDENT) + + def depart_field_list(self, node): + self.dedent() + + def visit_field_name(self, node): + if self._in_docinfo: + self._field_name = node.astext() + raise nodes.SkipNode + else: + self.body.append(self.defs['field_name'][0]) + + def depart_field_name(self, node): + self.body.append(self.defs['field_name'][1]) + + def visit_figure(self, node): + self.indent(2.5) + self.indent(0) + + def depart_figure(self, node): + self.dedent() + self.dedent() + + def visit_footer(self, node): + self.document.reporter.warning('"footer" not supported', + base_node=node) + + def depart_footer(self, node): + pass + + def visit_footnote(self, node): + num, text = node.astext().split(None, 1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % self.deunicode(num)) + + def depart_footnote(self, node): + pass + + def footnote_backrefs(self, node): + self.document.reporter.warning('"footnote_backrefs" not supported', + base_node=node) + + def visit_footnote_reference(self, node): + self.body.append('['+self.deunicode(node.astext())+']') + raise nodes.SkipNode + + def depart_footnote_reference(self, node): + pass + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + raise NotImplementedError(node.astext()) + + def depart_header(self, node): + pass + + def visit_hint(self, node): + self.visit_admonition(node, 'hint') + + depart_hint = depart_admonition + + def visit_subscript(self, node): + self.body.append('\\s-2\\d') + + def depart_subscript(self, node): + self.body.append('\\u\\s0') + + def visit_superscript(self, node): + self.body.append('\\s-2\\u') + + def depart_superscript(self, node): + self.body.append('\\d\\s0') + + def visit_attribution(self, node): + self.body.append('\\(em ') + + def depart_attribution(self, node): + self.body.append('\n') + + def visit_image(self, node): + self.document.reporter.warning('"image" not supported', + base_node=node) + text = [] + if 'alt' in node.attributes: + text.append(node.attributes['alt']) + if 'uri' in node.attributes: + text.append(node.attributes['uri']) + self.body.append('[image: %s]\n' % ('/'.join(text))) + raise nodes.SkipNode + + def visit_important(self, node): + self.visit_admonition(node, 'important') + + depart_important = depart_admonition + + def visit_label(self, node): + # footnote and citation + if (isinstance(node.parent, nodes.footnote) + or isinstance(node.parent, nodes.citation)): + raise nodes.SkipNode + self.document.reporter.warning('"unsupported "label"', + base_node=node) + self.body.append('[') + + def depart_label(self, node): + self.body.append(']\n') + + def visit_legend(self, node): + pass + + def depart_legend(self, node): + pass + + # WHAT should we use .INDENT, .UNINDENT ? + def visit_line_block(self, node): + self._line_block += 1 + if self._line_block == 1: + # TODO: separate inline blocks from previous paragraphs + # see http://hg.intevation.org/mercurial/crew/rev/9c142ed9c405 + # self.body.append('.sp\n') + # but it does not work for me. + self.body.append('.nf\n') + else: + self.body.append('.in +2\n') + + def depart_line_block(self, node): + self._line_block -= 1 + if self._line_block == 0: + self.body.append('.fi\n') + self.body.append('.sp\n') + else: + self.body.append('.in -2\n') + + def visit_line(self, node): + pass + + def depart_line(self, node): + self.body.append('\n') + + def visit_list_item(self, node): + # man 7 man argues to use ".IP" instead of ".TP" + self.body.append('.IP %s %d\n' % ( + next(self._list_char[-1]), + self._list_char[-1].get_width(),)) + + def depart_list_item(self, node): + pass + + def visit_literal(self, node): + self.body.append(self.defs['literal'][0]) + + def depart_literal(self, node): + self.body.append(self.defs['literal'][1]) + + def visit_literal_block(self, node): + # BUG/HACK: indent alway uses the _last_ indention, + # thus we need two of them. + self.indent(LITERAL_BLOCK_INDENT) + self.indent(0) + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_literal_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + self.dedent() + self.dedent() + + def visit_math(self, node): + self.document.reporter.warning('"math" role not supported', + base_node=node) + self.visit_literal(node) + + def depart_math(self, node): + self.depart_literal(node) + + def visit_math_block(self, node): + self.document.reporter.warning('"math" directive not supported', + base_node=node) + self.visit_literal_block(node) + + def depart_math_block(self, node): + self.depart_literal_block(node) + + def visit_meta(self, node): + raise NotImplementedError(node.astext()) + + def depart_meta(self, node): + pass + + def visit_note(self, node): + self.visit_admonition(node, 'note') + + depart_note = depart_admonition + + def indent(self, by=0.5): + # if we are in a section ".SH" there already is a .RS + step = self._indent[-1] + self._indent.append(by) + self.body.append(self.defs['indent'][0] % step) + + def dedent(self): + self._indent.pop() + self.body.append(self.defs['indent'][1]) + + def visit_option_list(self, node): + self.indent(OPTION_LIST_INDENT) + + def depart_option_list(self, node): + self.dedent() + + def visit_option_list_item(self, node): + # one item of the list + self.body.append(self.defs['option_list_item'][0]) + + def depart_option_list_item(self, node): + self.body.append(self.defs['option_list_item'][1]) + + def visit_option_group(self, node): + # as one option could have several forms it is a group + # options without parameter bold only, .B, -v + # options with parameter bold italic, .BI, -f file + # + # we do not know if .B or .BI + self.context.append('.B') # blind guess + self.context.append(len(self.body)) # to be able to insert later + self.context.append(0) # option counter + + def depart_option_group(self, node): + self.context.pop() # the counter + start_position = self.context.pop() + text = self.body[start_position:] + del self.body[start_position:] + self.body.append('%s%s\n' % (self.context.pop(), ''.join(text))) + + def visit_option(self, node): + # each form of the option will be presented separately + if self.context[-1] > 0: + self.body.append('\\fP,\\fB ') + if self.context[-3] == '.BI': + self.body.append('\\') + self.body.append(' ') + + def depart_option(self, node): + self.context[-1] += 1 + + def visit_option_string(self, node): + # do not know if .B or .BI + pass + + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + self.context[-3] = '.BI' # bold/italic alternate + if node['delimiter'] != ' ': + self.body.append('\\fB%s ' % node['delimiter']) + elif self.body[len(self.body)-1].endswith('='): + # a blank only means no blank in output, just changing font + self.body.append(' ') + else: + # blank backslash blank, switch font then a blank + self.body.append(' \\ ') + + def depart_option_argument(self, node): + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + pass + + def first_child(self, node): + first = isinstance(node.parent[0], nodes.label) # skip label + for child in node.parent.children[first:]: + if isinstance(child, nodes.Invisible): + continue + if child is node: + return 1 + break + return 0 + + def visit_paragraph(self, node): + # ``.PP`` : Start standard indented paragraph. + # ``.LP`` : Start block paragraph, all except the first. + # ``.P [type]`` : Start paragraph type. + # NOTE dont use paragraph starts because they reset indentation. + # ``.sp`` is only vertical space + self.ensure_eol() + if not self.first_child(node): + self.body.append('.sp\n') + + def depart_paragraph(self, node): + self.body.append('\n') + + def visit_problematic(self, node): + self.body.append(self.defs['problematic'][0]) + + def depart_problematic(self, node): + self.body.append(self.defs['problematic'][1]) + + def visit_raw(self, node): + if node.get('format') == 'manpage': + self.body.append(node.astext() + "\n") + # Keep non-manpage raw text out of output: + raise nodes.SkipNode + + def visit_reference(self, node): + """E.g. link or email address.""" + self.body.append(self.defs['reference'][0]) + + def depart_reference(self, node): + self.body.append(self.defs['reference'][1]) + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision') + + depart_revision = depart_docinfo_item + + def visit_row(self, node): + self._active_table.new_row() + + def depart_row(self, node): + pass + + def visit_section(self, node): + self.section_level += 1 + + def depart_section(self, node): + self.section_level -= 1 + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status') + + depart_status = depart_docinfo_item + + def visit_strong(self, node): + self.body.append(self.defs['strong'][0]) + + def depart_strong(self, node): + self.body.append(self.defs['strong'][1]) + + def visit_substitution_definition(self, node): + """Internal only.""" + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.document.reporter.warning('"substitution_reference" not supported', + base_node=node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['strong'][0]) + elif isinstance(node.parent, nodes.document): + self.visit_docinfo_item(node, 'subtitle') + elif isinstance(node.parent, nodes.section): + self.body.append(self.defs['strong'][0]) + + def depart_subtitle(self, node): + # document subtitle calls SkipNode + self.body.append(self.defs['strong'][1]+'\n.PP\n') + + def visit_system_message(self, node): + # TODO add report_level + #if node['level'] < self.document.reporter['writer'].report_level: + # Level is too low to display: + # raise nodes.SkipNode + attr = {} + backref_text = '' + if node.hasattr('id'): + attr['name'] = node['id'] + if node.hasattr('line'): + line = ', line %s' % node['line'] + else: + line = '' + self.body.append('.IP "System Message: %s/%s (%s:%s)"\n' + % (node['type'], node['level'], node['source'], line)) + + def depart_system_message(self, node): + pass + + def visit_table(self, node): + self._active_table = Table() + + def depart_table(self, node): + self.ensure_eol() + self.body.extend(self._active_table.as_list()) + self._active_table = None + + def visit_target(self, node): + # targets are in-document hyper targets, without any use for man-pages. + raise nodes.SkipNode + + def visit_tbody(self, node): + pass + + def depart_tbody(self, node): + pass + + def visit_term(self, node): + self.body.append(self.defs['term'][0]) + + def depart_term(self, node): + self.body.append(self.defs['term'][1]) + + def visit_tgroup(self, node): + pass + + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + # MAYBE double line '=' + pass + + def depart_thead(self, node): + # MAYBE double line '=' + pass + + def visit_tip(self, node): + self.visit_admonition(node, 'tip') + + depart_tip = depart_admonition + + def visit_title(self, node): + if isinstance(node.parent, nodes.topic): + self.body.append(self.defs['topic-title'][0]) + elif isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['sidebar-title'][0]) + elif isinstance(node.parent, nodes.admonition): + self.body.append('.IP "') + elif self.section_level == 0: + self._docinfo['title'] = node.astext() + # document title for .TH + self._docinfo['title_upper'] = node.astext().upper() + raise nodes.SkipNode + elif self.section_level == 1: + self.body.append('.SH %s\n' % self.deunicode(node.astext().upper())) + raise nodes.SkipNode + else: + self.body.append('.SS ') + + def depart_title(self, node): + if isinstance(node.parent, nodes.admonition): + self.body.append('"') + self.body.append('\n') + + def visit_title_reference(self, node): + """inline citation reference""" + self.body.append(self.defs['title_reference'][0]) + + def depart_title_reference(self, node): + self.body.append(self.defs['title_reference'][1]) + + def visit_topic(self, node): + pass + + def depart_topic(self, node): + pass + + def visit_sidebar(self, node): + pass + + def depart_sidebar(self, node): + pass + + def visit_rubric(self, node): + pass + + def depart_rubric(self, node): + pass + + def visit_transition(self, node): + # .PP Begin a new paragraph and reset prevailing indent. + # .sp N leaves N lines of blank space. + # .ce centers the next line + self.body.append('\n.sp\n.ce\n----\n') + + def depart_transition(self, node): + self.body.append('\n.ce 0\n.sp\n') + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version') + + def visit_warning(self, node): + self.visit_admonition(node, 'warning') + + depart_warning = depart_admonition + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' + % node.__class__.__name__) + +# vim: set fileencoding=utf-8 et ts=4 ai : diff --git a/python/helpers/py3only/docutils/writers/null.py b/python/helpers/py3only/docutils/writers/null.py new file mode 100644 index 000000000000..b870788a4490 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/null.py @@ -0,0 +1,21 @@ +# $Id: null.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A do-nothing Writer. +""" + +from docutils import writers + + +class Writer(writers.UnfilteredWriter): + + supported = ('null',) + """Formats this writer supports.""" + + config_section = 'null writer' + config_section_dependencies = ('writers',) + + def translate(self): + pass diff --git a/python/helpers/py3only/docutils/writers/odf_odt/__init__.py b/python/helpers/py3only/docutils/writers/odf_odt/__init__.py new file mode 100644 index 000000000000..91c1a3cda1ac --- /dev/null +++ b/python/helpers/py3only/docutils/writers/odf_odt/__init__.py @@ -0,0 +1,3297 @@ +# $Id: __init__.py 7717 2013-08-21 22:01:21Z milde $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Open Document Format (ODF) Writer. + +""" + +VERSION = '1.0a' + +__docformat__ = 'reStructuredText' + +import copy +import io +import os +import os.path +import re +import sys +import tempfile +import time +import zipfile +from xml.dom import minidom + +import urllib.error +import urllib.parse +import urllib.request + +import docutils +from docutils import frontend, nodes, utils, writers, languages +from docutils.readers import standalone +from docutils.transforms import references + +WhichElementTree = '' +try: + # 1. Try to use lxml. + #from lxml import etree + #WhichElementTree = 'lxml' + raise ImportError('Ignoring lxml') +except ImportError as e: + try: + # 2. Try to use ElementTree from the Python standard library. + from xml.etree import ElementTree as etree + WhichElementTree = 'elementtree' + except ImportError as e: + try: + # 3. Try to use a version of ElementTree installed as a separate + # product. + from elementtree import ElementTree as etree + WhichElementTree = 'elementtree' + except ImportError as e: + s1 = 'Must install either a version of Python containing ' \ + 'ElementTree (Python version >=2.5) or install ElementTree.' + raise ImportError(s1) + +# +# Import pygments and odtwriter pygments formatters if possible. +try: + import pygments + import pygments.lexers + from .pygmentsformatter import OdtPygmentsProgFormatter, \ + OdtPygmentsLaTeXFormatter +except ImportError as exp: + pygments = None + +# check for the Python Imaging Library +try: + import PIL.Image +except ImportError: + try: # sometimes PIL modules are put in PYTHONPATH's root + import Image + class PIL(object): pass # dummy wrapper + PIL.Image = Image + except ImportError: + PIL = None + +## import warnings +## warnings.warn('importing IPShellEmbed', UserWarning) +## from IPython.Shell import IPShellEmbed +## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ', +## '-po', 'Out<\\#>: ', '-nosep'] +## ipshell = IPShellEmbed(args, +## banner = 'Entering IPython. Press Ctrl-D to exit.', +## exit_msg = 'Leaving Interpreter, back to program.') + + +# +# ElementTree does not support getparent method (lxml does). +# This wrapper class and the following support functions provide +# that support for the ability to get the parent of an element. +# +if WhichElementTree == 'elementtree': + import weakref + _parents = weakref.WeakKeyDictionary() + if isinstance(etree.Element, type): + _ElementInterface = etree.Element + else: + _ElementInterface = etree._ElementInterface + class _ElementInterfaceWrapper(_ElementInterface): + def __init__(self, tag, attrib=None): + _ElementInterface.__init__(self, tag, attrib) + _parents[self] = None + def setparent(self, parent): + _parents[self] = parent + def getparent(self): + return _parents[self] + + +# +# Constants and globals + +SPACES_PATTERN = re.compile(r'( +)') +TABS_PATTERN = re.compile(r'(\t+)') +FILL_PAT1 = re.compile(r'^ +') +FILL_PAT2 = re.compile(r' {2,}') + +TABLESTYLEPREFIX = 'rststyle-table-' +TABLENAMEDEFAULT = '%s0' % TABLESTYLEPREFIX +TABLEPROPERTYNAMES = ('border', 'border-top', 'border-left', + 'border-right', 'border-bottom', ) + +GENERATOR_DESC = 'Docutils.org/odf_odt' + +NAME_SPACE_1 = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0' + +CONTENT_NAMESPACE_DICT = CNSD = { +# 'office:version': '1.0', + 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dom': 'http://www.w3.org/2001/xml-events', + 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'math': 'http://www.w3.org/1998/Math/MathML', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'office': NAME_SPACE_1, + 'ooo': 'http://openoffice.org/2004/office', + 'oooc': 'http://openoffice.org/2004/calc', + 'ooow': 'http://openoffice.org/2004/writer', + 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + + 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xforms': 'http://www.w3.org/2002/xforms', + 'xlink': 'http://www.w3.org/1999/xlink', + 'xsd': 'http://www.w3.org/2001/XMLSchema', + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + } + +STYLES_NAMESPACE_DICT = SNSD = { +# 'office:version': '1.0', + 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dom': 'http://www.w3.org/2001/xml-events', + 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'math': 'http://www.w3.org/1998/Math/MathML', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'office': NAME_SPACE_1, + 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'ooo': 'http://openoffice.org/2004/office', + 'oooc': 'http://openoffice.org/2004/calc', + 'ooow': 'http://openoffice.org/2004/writer', + 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xlink': 'http://www.w3.org/1999/xlink', + } + +MANIFEST_NAMESPACE_DICT = MANNSD = { + 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0', +} + +META_NAMESPACE_DICT = METNSD = { +# 'office:version': '1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'office': NAME_SPACE_1, + 'ooo': 'http://openoffice.org/2004/office', + 'xlink': 'http://www.w3.org/1999/xlink', +} + +# +# Attribute dictionaries for use with ElementTree (not lxml), which +# does not support use of nsmap parameter on Element() and SubElement(). + +CONTENT_NAMESPACE_ATTRIB = { + #'office:version': '1.0', + 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:dom': 'http://www.w3.org/2001/xml-events', + 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'xmlns:math': 'http://www.w3.org/1998/Math/MathML', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:oooc': 'http://openoffice.org/2004/calc', + 'xmlns:ooow': 'http://openoffice.org/2004/writer', + 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xmlns:xforms': 'http://www.w3.org/2002/xforms', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema', + 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', + } + +STYLES_NAMESPACE_ATTRIB = { + #'office:version': '1.0', + 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:dom': 'http://www.w3.org/2001/xml-events', + 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'xmlns:math': 'http://www.w3.org/1998/Math/MathML', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:oooc': 'http://openoffice.org/2004/calc', + 'xmlns:ooow': 'http://openoffice.org/2004/writer', + 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + } + +MANIFEST_NAMESPACE_ATTRIB = { + 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0', +} + +META_NAMESPACE_ATTRIB = { + #'office:version': '1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', +} + + +# +# Functions +# + +# +# ElementTree support functions. +# In order to be able to get the parent of elements, must use these +# instead of the functions with same name provided by ElementTree. +# +def Element(tag, attrib=None, nsmap=None, nsdict=CNSD): + if attrib is None: + attrib = {} + tag, attrib = fix_ns(tag, attrib, nsdict) + if WhichElementTree == 'lxml': + el = etree.Element(tag, attrib, nsmap=nsmap) + else: + el = _ElementInterfaceWrapper(tag, attrib) + return el + +def SubElement(parent, tag, attrib=None, nsmap=None, nsdict=CNSD): + if attrib is None: + attrib = {} + tag, attrib = fix_ns(tag, attrib, nsdict) + if WhichElementTree == 'lxml': + el = etree.SubElement(parent, tag, attrib, nsmap=nsmap) + else: + el = _ElementInterfaceWrapper(tag, attrib) + parent.append(el) + el.setparent(parent) + return el + +def fix_ns(tag, attrib, nsdict): + nstag = add_ns(tag, nsdict) + nsattrib = {} + for key, val in attrib.items(): + nskey = add_ns(key, nsdict) + nsattrib[nskey] = val + return nstag, nsattrib + +def add_ns(tag, nsdict=CNSD): + if WhichElementTree == 'lxml': + nstag, name = tag.split(':') + ns = nsdict.get(nstag) + if ns is None: + raise RuntimeError('Invalid namespace prefix: %s' % nstag) + tag = '{%s}%s' % (ns, name,) + return tag + +def ToString(et): + outstream = io.StringIO() + if sys.version_info >= (3, 2): + et.write(outstream, encoding="unicode") + else: + et.write(outstream) + s1 = outstream.getvalue() + outstream.close() + return s1 + + +def escape_cdata(text): + text = text.replace("&", "&") + text = text.replace("<", "<") + text = text.replace(">", ">") + ascii = '' + for char in text: + if ord(char) >= ord("\x7f"): + ascii += "&#x%X;" % ( ord(char), ) + else: + ascii += char + return ascii + + + +WORD_SPLIT_PAT1 = re.compile(r'\b(\w*)\b\W*') + +def split_words(line): + # We need whitespace at the end of the string for our regexpr. + line += ' ' + words = [] + pos1 = 0 + mo = WORD_SPLIT_PAT1.search(line, pos1) + while mo is not None: + word = mo.groups()[0] + words.append(word) + pos1 = mo.end() + mo = WORD_SPLIT_PAT1.search(line, pos1) + return words + + +# +# Classes +# + + +class TableStyle(object): + def __init__(self, border=None, backgroundcolor=None): + self.border = border + self.backgroundcolor = backgroundcolor + def get_border_(self): + return self.border_ + def set_border_(self, border): + self.border_ = border + border = property(get_border_, set_border_) + def get_backgroundcolor_(self): + return self.backgroundcolor_ + def set_backgroundcolor_(self, backgroundcolor): + self.backgroundcolor_ = backgroundcolor + backgroundcolor = property(get_backgroundcolor_, set_backgroundcolor_) + +BUILTIN_DEFAULT_TABLE_STYLE = TableStyle( + border = '0.0007in solid #000000') + +# +# Information about the indentation level for lists nested inside +# other contexts, e.g. dictionary lists. +class ListLevel(object): + def __init__(self, level, sibling_level=True, nested_level=True): + self.level = level + self.sibling_level = sibling_level + self.nested_level = nested_level + def set_sibling(self, sibling_level): self.sibling_level = sibling_level + def get_sibling(self): return self.sibling_level + def set_nested(self, nested_level): self.nested_level = nested_level + def get_nested(self): return self.nested_level + def set_level(self, level): self.level = level + def get_level(self): return self.level + + +class Writer(writers.Writer): + + MIME_TYPE = 'application/vnd.oasis.opendocument.text' + EXTENSION = '.odt' + + supported = ('odt', ) + """Formats this writer supports.""" + + default_stylesheet = 'styles' + EXTENSION + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + default_template = 'template.txt' + + default_template_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_template)) + + settings_spec = ( + 'ODF-Specific Options', + None, + ( + ('Specify a stylesheet. ' + 'Default: "%s"' % default_stylesheet_path, + ['--stylesheet'], + { + 'default': default_stylesheet_path, + 'dest': 'stylesheet' + }), + ('Specify a configuration/mapping file relative to the ' + 'current working ' + 'directory for additional ODF options. ' + 'In particular, this file may contain a section named ' + '"Formats" that maps default style names to ' + 'names to be used in the resulting output file allowing for ' + 'adhering to external standards. ' + 'For more info and the format of the configuration/mapping file, ' + 'see the odtwriter doc.', + ['--odf-config-file'], + {'metavar': ''}), + ('Obfuscate email addresses to confuse harvesters while still ' + 'keeping email links usable with standards-compliant browsers.', + ['--cloak-email-addresses'], + {'default': False, + 'action': 'store_true', + 'dest': 'cloak_email_addresses', + 'validator': frontend.validate_boolean}), + ('Do not obfuscate email addresses.', + ['--no-cloak-email-addresses'], + {'default': False, + 'action': 'store_false', + 'dest': 'cloak_email_addresses', + 'validator': frontend.validate_boolean}), + ('Specify the thickness of table borders in thousands of a cm. ' + 'Default is 35.', + ['--table-border-thickness'], + {'default': None, + 'validator': frontend.validate_nonnegative_int}), + ('Add syntax highlighting in literal code blocks.', + ['--add-syntax-highlighting'], + {'default': False, + 'action': 'store_true', + 'dest': 'add_syntax_highlighting', + 'validator': frontend.validate_boolean}), + ('Do not add syntax highlighting in literal code blocks. (default)', + ['--no-syntax-highlighting'], + {'default': False, + 'action': 'store_false', + 'dest': 'add_syntax_highlighting', + 'validator': frontend.validate_boolean}), + ('Create sections for headers. (default)', + ['--create-sections'], + {'default': True, + 'action': 'store_true', + 'dest': 'create_sections', + 'validator': frontend.validate_boolean}), + ('Do not create sections for headers.', + ['--no-sections'], + {'default': True, + 'action': 'store_false', + 'dest': 'create_sections', + 'validator': frontend.validate_boolean}), + ('Create links.', + ['--create-links'], + {'default': False, + 'action': 'store_true', + 'dest': 'create_links', + 'validator': frontend.validate_boolean}), + ('Do not create links. (default)', + ['--no-links'], + {'default': False, + 'action': 'store_false', + 'dest': 'create_links', + 'validator': frontend.validate_boolean}), + ('Generate endnotes at end of document, not footnotes ' + 'at bottom of page.', + ['--endnotes-end-doc'], + {'default': False, + 'action': 'store_true', + 'dest': 'endnotes_end_doc', + 'validator': frontend.validate_boolean}), + ('Generate footnotes at bottom of page, not endnotes ' + 'at end of document. (default)', + ['--no-endnotes-end-doc'], + {'default': False, + 'action': 'store_false', + 'dest': 'endnotes_end_doc', + 'validator': frontend.validate_boolean}), + ('Generate a bullet list table of contents, not ' + 'an ODF/oowriter table of contents.', + ['--generate-list-toc'], + {'default': True, + 'action': 'store_false', + 'dest': 'generate_oowriter_toc', + 'validator': frontend.validate_boolean}), + ('Generate an ODF/oowriter table of contents, not ' + 'a bullet list. (default)', + ['--generate-oowriter-toc'], + {'default': True, + 'action': 'store_true', + 'dest': 'generate_oowriter_toc', + 'validator': frontend.validate_boolean}), + ('Specify the contents of an custom header line. ' + 'See odf_odt writer documentation for details ' + 'about special field character sequences.', + ['--custom-odt-header'], + { 'default': '', + 'dest': 'custom_header', + }), + ('Specify the contents of an custom footer line. ' + 'See odf_odt writer documentation for details ' + 'about special field character sequences.', + ['--custom-odt-footer'], + { 'default': '', + 'dest': 'custom_footer', + }), + ) + ) + + settings_defaults = { + 'output_encoding_error_handler': 'xmlcharrefreplace', + } + + relative_path_settings = ( + 'stylesheet_path', + ) + + config_section = 'odf_odt writer' + config_section_dependencies = ( + 'writers', + ) + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = ODFTranslator + + def translate(self): + self.settings = self.document.settings + self.visitor = self.translator_class(self.document) + self.visitor.retrieve_styles(self.EXTENSION) + self.document.walkabout(self.visitor) + self.visitor.add_doc_title() + self.assemble_my_parts() + self.output = self.parts['whole'] + + def assemble_my_parts(self): + """Assemble the `self.parts` dictionary. Extend in subclasses. + """ + writers.Writer.assemble_parts(self) + f = tempfile.NamedTemporaryFile() + zfile = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) + self.write_zip_str(zfile, 'mimetype', self.MIME_TYPE, + compress_type=zipfile.ZIP_STORED) + content = self.visitor.content_astext() + self.write_zip_str(zfile, 'content.xml', content) + s1 = self.create_manifest() + self.write_zip_str(zfile, 'META-INF/manifest.xml', s1) + s1 = self.create_meta() + self.write_zip_str(zfile, 'meta.xml', s1) + s1 = self.get_stylesheet() + self.write_zip_str(zfile, 'styles.xml', s1) + self.store_embedded_files(zfile) + self.copy_from_stylesheet(zfile) + zfile.close() + f.seek(0) + whole = f.read() + f.close() + self.parts['whole'] = whole + self.parts['encoding'] = self.document.settings.output_encoding + self.parts['version'] = docutils.__version__ + + def write_zip_str(self, zfile, name, bytes, compress_type=zipfile.ZIP_DEFLATED): + localtime = time.localtime(time.time()) + zinfo = zipfile.ZipInfo(name, localtime) + # Add some standard UNIX file access permissions (-rw-r--r--). + zinfo.external_attr = (0x81a4 & 0xFFFF) << 16 + zinfo.compress_type = compress_type + zfile.writestr(zinfo, bytes) + + def store_embedded_files(self, zfile): + embedded_files = self.visitor.get_embedded_file_list() + for source, destination in embedded_files: + if source is None: + continue + try: + # encode/decode + destination1 = destination.decode('latin-1').encode('utf-8') + zfile.write(source, destination1) + except OSError as e: + self.document.reporter.warning( + "Can't open file %s." % (source, )) + + def get_settings(self): + """ + modeled after get_stylesheet + """ + stylespath = self.settings.stylesheet + zfile = zipfile.ZipFile(stylespath, 'r') + s1 = zfile.read('settings.xml') + zfile.close() + return s1 + + def get_stylesheet(self): + """Get the stylesheet from the visitor. + Ask the visitor to setup the page. + """ + s1 = self.visitor.setup_page() + return s1 + + def copy_from_stylesheet(self, outzipfile): + """Copy images, settings, etc from the stylesheet doc into target doc. + """ + stylespath = self.settings.stylesheet + inzipfile = zipfile.ZipFile(stylespath, 'r') + # Copy the styles. + s1 = inzipfile.read('settings.xml') + self.write_zip_str(outzipfile, 'settings.xml', s1) + # Copy the images. + namelist = inzipfile.namelist() + for name in namelist: + if name.startswith('Pictures/'): + imageobj = inzipfile.read(name) + outzipfile.writestr(name, imageobj) + inzipfile.close() + + def assemble_parts(self): + pass + + def create_manifest(self): + if WhichElementTree == 'lxml': + root = Element('manifest:manifest', + nsmap=MANIFEST_NAMESPACE_DICT, + nsdict=MANIFEST_NAMESPACE_DICT, + ) + else: + root = Element('manifest:manifest', + attrib=MANIFEST_NAMESPACE_ATTRIB, + nsdict=MANIFEST_NAMESPACE_DICT, + ) + doc = etree.ElementTree(root) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': self.MIME_TYPE, + 'manifest:full-path': '/', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'content.xml', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'styles.xml', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'settings.xml', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'meta.xml', + }, nsdict=MANNSD) + s1 = ToString(doc) + doc = minidom.parseString(s1) + s1 = doc.toprettyxml(' ') + return s1 + + def create_meta(self): + if WhichElementTree == 'lxml': + root = Element('office:document-meta', + nsmap=META_NAMESPACE_DICT, + nsdict=META_NAMESPACE_DICT, + ) + else: + root = Element('office:document-meta', + attrib=META_NAMESPACE_ATTRIB, + nsdict=META_NAMESPACE_DICT, + ) + doc = etree.ElementTree(root) + root = SubElement(root, 'office:meta', nsdict=METNSD) + el1 = SubElement(root, 'meta:generator', nsdict=METNSD) + el1.text = 'Docutils/rst2odf.py/%s' % (VERSION, ) + s1 = os.environ.get('USER', '') + el1 = SubElement(root, 'meta:initial-creator', nsdict=METNSD) + el1.text = s1 + s2 = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime()) + el1 = SubElement(root, 'meta:creation-date', nsdict=METNSD) + el1.text = s2 + el1 = SubElement(root, 'dc:creator', nsdict=METNSD) + el1.text = s1 + el1 = SubElement(root, 'dc:date', nsdict=METNSD) + el1.text = s2 + el1 = SubElement(root, 'dc:language', nsdict=METNSD) + el1.text = 'en-US' + el1 = SubElement(root, 'meta:editing-cycles', nsdict=METNSD) + el1.text = '1' + el1 = SubElement(root, 'meta:editing-duration', nsdict=METNSD) + el1.text = 'PT00M01S' + title = self.visitor.get_title() + el1 = SubElement(root, 'dc:title', nsdict=METNSD) + if title: + el1.text = title + else: + el1.text = '[no title]' + meta_dict = self.visitor.get_meta_dict() + keywordstr = meta_dict.get('keywords') + if keywordstr is not None: + keywords = split_words(keywordstr) + for keyword in keywords: + el1 = SubElement(root, 'meta:keyword', nsdict=METNSD) + el1.text = keyword + description = meta_dict.get('description') + if description is not None: + el1 = SubElement(root, 'dc:description', nsdict=METNSD) + el1.text = description + s1 = ToString(doc) + #doc = minidom.parseString(s1) + #s1 = doc.toprettyxml(' ') + return s1 + +# class ODFTranslator(nodes.SparseNodeVisitor): + +class ODFTranslator(nodes.GenericNodeVisitor): + + used_styles = ( + 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem', + 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist', + 'bulletitem', 'bulletlist', + 'caption', 'legend', + 'centeredtextbody', 'codeblock', 'codeblock-indented', + 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname', + 'codeblock-keyword', 'codeblock-name', 'codeblock-number', + 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem', + 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist', + 'epigraph-enumitem', 'epigraph-enumlist', 'footer', + 'footnote', 'citation', + 'header', 'highlights', 'highlights-bulletitem', + 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist', + 'horizontalline', 'inlineliteral', 'quotation', 'rubric', + 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist', + 'title', + 'subtitle', + 'heading1', + 'heading2', + 'heading3', + 'heading4', + 'heading5', + 'heading6', + 'heading7', + 'admon-attention-hdr', + 'admon-attention-body', + 'admon-caution-hdr', + 'admon-caution-body', + 'admon-danger-hdr', + 'admon-danger-body', + 'admon-error-hdr', + 'admon-error-body', + 'admon-generic-hdr', + 'admon-generic-body', + 'admon-hint-hdr', + 'admon-hint-body', + 'admon-important-hdr', + 'admon-important-body', + 'admon-note-hdr', + 'admon-note-body', + 'admon-tip-hdr', + 'admon-tip-body', + 'admon-warning-hdr', + 'admon-warning-body', + 'tableoption', + 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c', + 'Table%d.%c%d', + 'lineblock1', + 'lineblock2', + 'lineblock3', + 'lineblock4', + 'lineblock5', + 'lineblock6', + 'image', 'figureframe', + ) + + def __init__(self, document): + #nodes.SparseNodeVisitor.__init__(self, document) + nodes.GenericNodeVisitor.__init__(self, document) + self.settings = document.settings + lcode = self.settings.language_code + self.language = languages.get_language(lcode, document.reporter) + self.format_map = { } + if self.settings.odf_config_file: + from configparser import ConfigParser + + parser = ConfigParser() + parser.read(self.settings.odf_config_file) + for rststyle, format in parser.items("Formats"): + if rststyle not in self.used_styles: + self.document.reporter.warning( + 'Style "%s" is not a style used by odtwriter.' % ( + rststyle, )) + self.format_map[rststyle] = format.decode('utf-8') + self.section_level = 0 + self.section_count = 0 + # Create ElementTree content and styles documents. + if WhichElementTree == 'lxml': + root = Element( + 'office:document-content', + nsmap=CONTENT_NAMESPACE_DICT, + ) + else: + root = Element( + 'office:document-content', + attrib=CONTENT_NAMESPACE_ATTRIB, + ) + self.content_tree = etree.ElementTree(element=root) + self.current_element = root + SubElement(root, 'office:scripts') + SubElement(root, 'office:font-face-decls') + el = SubElement(root, 'office:automatic-styles') + self.automatic_styles = el + el = SubElement(root, 'office:body') + el = self.generate_content_element(el) + self.current_element = el + self.body_text_element = el + self.paragraph_style_stack = [self.rststyle('textbody'), ] + self.list_style_stack = [] + self.table_count = 0 + self.column_count = ord('A') - 1 + self.trace_level = -1 + self.optiontablestyles_generated = False + self.field_name = None + self.field_element = None + self.title = None + self.image_count = 0 + self.image_style_count = 0 + self.image_dict = {} + self.embedded_file_list = [] + self.syntaxhighlighting = 1 + self.syntaxhighlight_lexer = 'python' + self.header_content = [] + self.footer_content = [] + self.in_header = False + self.in_footer = False + self.blockstyle = '' + self.in_table_of_contents = False + self.table_of_content_index_body = None + self.list_level = 0 + self.def_list_level = 0 + self.footnote_ref_dict = {} + self.footnote_list = [] + self.footnote_chars_idx = 0 + self.footnote_level = 0 + self.pending_ids = [ ] + self.in_paragraph = False + self.found_doc_title = False + self.bumped_list_level_stack = [] + self.meta_dict = {} + self.line_block_level = 0 + self.line_indent_level = 0 + self.citation_id = None + self.style_index = 0 # use to form unique style names + self.str_stylesheet = '' + self.str_stylesheetcontent = '' + self.dom_stylesheet = None + self.table_styles = None + self.in_citation = False + + + def get_str_stylesheet(self): + return self.str_stylesheet + + def retrieve_styles(self, extension): + """Retrieve the stylesheet from either a .xml file or from + a .odt (zip) file. Return the content as a string. + """ + s2 = None + stylespath = self.settings.stylesheet + ext = os.path.splitext(stylespath)[1] + if ext == '.xml': + stylesfile = open(stylespath, 'r') + s1 = stylesfile.read() + stylesfile.close() + elif ext == extension: + zfile = zipfile.ZipFile(stylespath, 'r') + s1 = zfile.read('styles.xml') + s2 = zfile.read('content.xml') + zfile.close() + else: + raise RuntimeError('stylesheet path (%s) must be %s or .xml file' %(stylespath, extension)) + self.str_stylesheet = s1 + self.str_stylesheetcontent = s2 + self.dom_stylesheet = etree.fromstring(self.str_stylesheet) + self.dom_stylesheetcontent = etree.fromstring(self.str_stylesheetcontent) + self.table_styles = self.extract_table_styles(s2) + + def extract_table_styles(self, styles_str): + root = etree.fromstring(styles_str) + table_styles = {} + auto_styles = root.find( + '{%s}automatic-styles' % (CNSD['office'], )) + for stylenode in auto_styles: + name = stylenode.get('{%s}name' % (CNSD['style'], )) + tablename = name.split('.')[0] + family = stylenode.get('{%s}family' % (CNSD['style'], )) + if name.startswith(TABLESTYLEPREFIX): + tablestyle = table_styles.get(tablename) + if tablestyle is None: + tablestyle = TableStyle() + table_styles[tablename] = tablestyle + if family == 'table': + properties = stylenode.find( + '{%s}table-properties' % (CNSD['style'], )) + property = properties.get('{%s}%s' % (CNSD['fo'], + 'background-color', )) + if property is not None and property != 'none': + tablestyle.backgroundcolor = property + elif family == 'table-cell': + properties = stylenode.find( + '{%s}table-cell-properties' % (CNSD['style'], )) + if properties is not None: + border = self.get_property(properties) + if border is not None: + tablestyle.border = border + return table_styles + + def get_property(self, stylenode): + border = None + for propertyname in TABLEPROPERTYNAMES: + border = stylenode.get('{%s}%s' % (CNSD['fo'], propertyname, )) + if border is not None and border != 'none': + return border + return border + + def add_doc_title(self): + text = self.settings.title + if text: + self.title = text + if not self.found_doc_title: + el = Element('text:p', attrib = { + 'text:style-name': self.rststyle('title'), + }) + el.text = text + self.body_text_element.insert(0, el) + el = self.find_first_text_p(self.body_text_element) + if el is not None: + self.attach_page_style(el) + + def find_first_text_p(self, el): + """Search the generated doc and return the first element. + """ + if ( + el.tag == 'text:p' or + el.tag == 'text:h' + ): + return el + elif el.getchildren(): + for child in el.getchildren(): + el1 = self.find_first_text_p(child) + if el1 is not None: + return el1 + return None + else: + return None + + def attach_page_style(self, el): + """Attach the default page style. + + Create an automatic-style that refers to the current style + of this element and that refers to the default page style. + """ + current_style = el.get('text:style-name') + style_name = 'P1003' + el1 = SubElement( + self.automatic_styles, 'style:style', attrib={ + 'style:name': style_name, + 'style:master-page-name': "rststyle-pagedefault", + 'style:family': "paragraph", + }, nsdict=SNSD) + if current_style: + el1.set('style:parent-style-name', current_style) + el.set('text:style-name', style_name) + + + def rststyle(self, name, parameters=( )): + """ + Returns the style name to use for the given style. + + If `parameters` is given `name` must contain a matching number of ``%`` and + is used as a format expression with `parameters` as the value. + """ + name1 = name % parameters + stylename = self.format_map.get(name1, 'rststyle-%s' % name1) + return stylename + + def generate_content_element(self, root): + return SubElement(root, 'office:text') + + def setup_page(self): + self.setup_paper(self.dom_stylesheet) + if (len(self.header_content) > 0 or len(self.footer_content) > 0 or + self.settings.custom_header or self.settings.custom_footer): + self.add_header_footer(self.dom_stylesheet) + new_content = etree.tostring(self.dom_stylesheet) + return new_content + + def setup_paper(self, root_el): + try: + fin = os.popen("paperconf -s 2> /dev/null") + w, h = list(map(float, fin.read().split())) + fin.close() + except: + w, h = 612, 792 # default to Letter + def walk(el): + if el.tag == "{%s}page-layout-properties" % SNSD["style"] and \ + "{%s}page-width" % SNSD["fo"] not in el.attrib: + el.attrib["{%s}page-width" % SNSD["fo"]] = "%.3fpt" % w + el.attrib["{%s}page-height" % SNSD["fo"]] = "%.3fpt" % h + el.attrib["{%s}margin-left" % SNSD["fo"]] = \ + el.attrib["{%s}margin-right" % SNSD["fo"]] = \ + "%.3fpt" % (.1 * w) + el.attrib["{%s}margin-top" % SNSD["fo"]] = \ + el.attrib["{%s}margin-bottom" % SNSD["fo"]] = \ + "%.3fpt" % (.1 * h) + else: + for subel in el.getchildren(): walk(subel) + walk(root_el) + + def add_header_footer(self, root_el): + automatic_styles = root_el.find( + '{%s}automatic-styles' % SNSD['office']) + path = '{%s}master-styles' % (NAME_SPACE_1, ) + master_el = root_el.find(path) + if master_el is None: + return + path = '{%s}master-page' % (SNSD['style'], ) + master_el_container = master_el.findall(path) + master_el = None + target_attrib = '{%s}name' % (SNSD['style'], ) + target_name = self.rststyle('pagedefault') + for el in master_el_container: + if el.get(target_attrib) == target_name: + master_el = el + break + if master_el is None: + return + el1 = master_el + if self.header_content or self.settings.custom_header: + if WhichElementTree == 'lxml': + el2 = SubElement(el1, 'style:header', nsdict=SNSD) + else: + el2 = SubElement(el1, 'style:header', + attrib=STYLES_NAMESPACE_ATTRIB, + nsdict=STYLES_NAMESPACE_DICT, + ) + for el in self.header_content: + attrkey = add_ns('text:style-name', nsdict=SNSD) + el.attrib[attrkey] = self.rststyle('header') + el2.append(el) + if self.settings.custom_header: + elcustom = self.create_custom_headfoot(el2, + self.settings.custom_header, 'header', automatic_styles) + if self.footer_content or self.settings.custom_footer: + if WhichElementTree == 'lxml': + el2 = SubElement(el1, 'style:footer', nsdict=SNSD) + else: + el2 = SubElement(el1, 'style:footer', + attrib=STYLES_NAMESPACE_ATTRIB, + nsdict=STYLES_NAMESPACE_DICT, + ) + for el in self.footer_content: + attrkey = add_ns('text:style-name', nsdict=SNSD) + el.attrib[attrkey] = self.rststyle('footer') + el2.append(el) + if self.settings.custom_footer: + elcustom = self.create_custom_headfoot(el2, + self.settings.custom_footer, 'footer', automatic_styles) + + code_none, code_field, code_text = list(range(3)) + field_pat = re.compile(r'%(..?)%') + + def create_custom_headfoot(self, parent, text, style_name, automatic_styles): + parent = SubElement(parent, 'text:p', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + current_element = None + field_iter = self.split_field_specifiers_iter(text) + for item in field_iter: + if item[0] == ODFTranslator.code_field: + if item[1] not in ('p', 'P', + 't1', 't2', 't3', 't4', + 'd1', 'd2', 'd3', 'd4', 'd5', + 's', 't', 'a'): + msg = 'bad field spec: %%%s%%' % (item[1], ) + raise RuntimeError(msg) + el1 = self.make_field_element(parent, + item[1], style_name, automatic_styles) + if el1 is None: + msg = 'bad field spec: %%%s%%' % (item[1], ) + raise RuntimeError(msg) + else: + current_element = el1 + else: + if current_element is None: + parent.text = item[1] + else: + current_element.tail = item[1] + + def make_field_element(self, parent, text, style_name, automatic_styles): + if text == 'p': + el1 = SubElement(parent, 'text:page-number', attrib={ + #'text:style-name': self.rststyle(style_name), + 'text:select-page': 'current', + }) + elif text == 'P': + el1 = SubElement(parent, 'text:page-count', attrib={ + #'text:style-name': self.rststyle(style_name), + }) + elif text == 't1': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + elif text == 't2': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:seconds', attrib={ + 'number:style': 'long', + }) + elif text == 't3': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:am-pm') + elif text == 't4': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:seconds', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:am-pm') + elif text == 'd1': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:day', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:year') + elif text == 'd2': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:day', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + elif text == 'd3': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:textual': 'true', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:day', attrib={ + }) + el3 = SubElement(el2, 'number:text') + el3.text = ', ' + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + elif text == 'd4': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:textual': 'true', + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:day', attrib={ + }) + el3 = SubElement(el2, 'number:text') + el3.text = ', ' + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + elif text == 'd5': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '-' + el3 = SubElement(el2, 'number:month', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '-' + el3 = SubElement(el2, 'number:day', attrib={ + 'number:style': 'long', + }) + elif text == 's': + el1 = SubElement(parent, 'text:subject', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + elif text == 't': + el1 = SubElement(parent, 'text:title', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + elif text == 'a': + el1 = SubElement(parent, 'text:author-name', attrib={ + 'text:fixed': 'false', + }) + else: + el1 = None + return el1 + + def split_field_specifiers_iter(self, text): + pos1 = 0 + pos_end = len(text) + while True: + mo = ODFTranslator.field_pat.search(text, pos1) + if mo: + pos2 = mo.start() + if pos2 > pos1: + yield (ODFTranslator.code_text, text[pos1:pos2]) + yield (ODFTranslator.code_field, mo.group(1)) + pos1 = mo.end() + else: + break + trailing = text[pos1:] + if trailing: + yield (ODFTranslator.code_text, trailing) + + + def astext(self): + root = self.content_tree.getroot() + et = etree.ElementTree(root) + s1 = ToString(et) + return s1 + + def content_astext(self): + return self.astext() + + def set_title(self, title): self.title = title + def get_title(self): return self.title + def set_embedded_file_list(self, embedded_file_list): + self.embedded_file_list = embedded_file_list + def get_embedded_file_list(self): return self.embedded_file_list + def get_meta_dict(self): return self.meta_dict + + def process_footnotes(self): + for node, el1 in self.footnote_list: + backrefs = node.attributes.get('backrefs', []) + first = True + for ref in backrefs: + el2 = self.footnote_ref_dict.get(ref) + if el2 is not None: + if first: + first = False + el3 = copy.deepcopy(el1) + el2.append(el3) + else: + children = el2.getchildren() + if len(children) > 0: # and 'id' in el2.attrib: + child = children[0] + ref1 = child.text + attribkey = add_ns('text:id', nsdict=SNSD) + id1 = el2.get(attribkey, 'footnote-error') + if id1 is None: + id1 = '' + tag = add_ns('text:note-ref', nsdict=SNSD) + el2.tag = tag + if self.settings.endnotes_end_doc: + note_class = 'endnote' + else: + note_class = 'footnote' + el2.attrib.clear() + attribkey = add_ns('text:note-class', nsdict=SNSD) + el2.attrib[attribkey] = note_class + attribkey = add_ns('text:ref-name', nsdict=SNSD) + el2.attrib[attribkey] = id1 + attribkey = add_ns('text:reference-format', nsdict=SNSD) + el2.attrib[attribkey] = 'page' + el2.text = ref1 + + # + # Utility methods + + def append_child(self, tag, attrib=None, parent=None): + if parent is None: + parent = self.current_element + if attrib is None: + el = SubElement(parent, tag) + else: + el = SubElement(parent, tag, attrib) + return el + + def append_p(self, style, text=None): + result = self.append_child('text:p', attrib={ + 'text:style-name': self.rststyle(style)}) + self.append_pending_ids(result) + if text is not None: + result.text = text + return result + + def append_pending_ids(self, el): + if self.settings.create_links: + for id in self.pending_ids: + SubElement(el, 'text:reference-mark', attrib={ + 'text:name': id}) + self.pending_ids = [ ] + + def set_current_element(self, el): + self.current_element = el + + def set_to_parent(self): + self.current_element = self.current_element.getparent() + + def generate_labeled_block(self, node, label): + label = '%s:' % (self.language.labels[label], ) + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + el = self.append_p('blockindent') + return el + + def generate_labeled_line(self, node, label): + label = '%s:' % (self.language.labels[label], ) + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + el1.tail = node.astext() + return el + + def encode(self, text): + text = text.replace('\u00a0', " ") + return text + + # + # Visitor functions + # + # In alphabetic order, more or less. + # See docutils.docutils.nodes.node_class_names. + # + + def dispatch_visit(self, node): + """Override to catch basic attributes which many nodes have.""" + self.handle_basic_atts(node) + nodes.GenericNodeVisitor.dispatch_visit(self, node) + + def handle_basic_atts(self, node): + if isinstance(node, nodes.Element) and node['ids']: + self.pending_ids += node['ids'] + + def default_visit(self, node): + self.document.reporter.warning('missing visit_%s' % (node.tagname, )) + + def default_departure(self, node): + self.document.reporter.warning('missing depart_%s' % (node.tagname, )) + + def visit_Text(self, node): + # Skip nodes whose text has been processed in parent nodes. + if isinstance(node.parent, docutils.nodes.literal_block): + return + text = node.astext() + # Are we in mixed content? If so, add the text to the + # etree tail of the previous sibling element. + if len(self.current_element.getchildren()) > 0: + if self.current_element.getchildren()[-1].tail: + self.current_element.getchildren()[-1].tail += text + else: + self.current_element.getchildren()[-1].tail = text + else: + if self.current_element.text: + self.current_element.text += text + else: + self.current_element.text = text + + def depart_Text(self, node): + pass + + # + # Pre-defined fields + # + + def visit_address(self, node): + el = self.generate_labeled_block(node, 'address') + self.set_current_element(el) + + def depart_address(self, node): + self.set_to_parent() + + def visit_author(self, node): + if isinstance(node.parent, nodes.authors): + el = self.append_p('blockindent') + else: + el = self.generate_labeled_block(node, 'author') + self.set_current_element(el) + + def depart_author(self, node): + self.set_to_parent() + + def visit_authors(self, node): + label = '%s:' % (self.language.labels['authors'], ) + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + + def depart_authors(self, node): + pass + + def visit_contact(self, node): + el = self.generate_labeled_block(node, 'contact') + self.set_current_element(el) + + def depart_contact(self, node): + self.set_to_parent() + + def visit_copyright(self, node): + el = self.generate_labeled_block(node, 'copyright') + self.set_current_element(el) + + def depart_copyright(self, node): + self.set_to_parent() + + def visit_date(self, node): + self.generate_labeled_line(node, 'date') + + def depart_date(self, node): + pass + + def visit_organization(self, node): + el = self.generate_labeled_block(node, 'organization') + self.set_current_element(el) + + def depart_organization(self, node): + self.set_to_parent() + + def visit_status(self, node): + el = self.generate_labeled_block(node, 'status') + self.set_current_element(el) + + def depart_status(self, node): + self.set_to_parent() + + def visit_revision(self, node): + el = self.generate_labeled_line(node, 'revision') + + def depart_revision(self, node): + pass + + def visit_version(self, node): + el = self.generate_labeled_line(node, 'version') + #self.set_current_element(el) + + def depart_version(self, node): + #self.set_to_parent() + pass + + def visit_attribution(self, node): + el = self.append_p('attribution', node.astext()) + + def depart_attribution(self, node): + pass + + def visit_block_quote(self, node): + if 'epigraph' in node.attributes['classes']: + self.paragraph_style_stack.append(self.rststyle('epigraph')) + self.blockstyle = self.rststyle('epigraph') + elif 'highlights' in node.attributes['classes']: + self.paragraph_style_stack.append(self.rststyle('highlights')) + self.blockstyle = self.rststyle('highlights') + else: + self.paragraph_style_stack.append(self.rststyle('blockquote')) + self.blockstyle = self.rststyle('blockquote') + self.line_indent_level += 1 + + def depart_block_quote(self, node): + self.paragraph_style_stack.pop() + self.blockstyle = '' + self.line_indent_level -= 1 + + def visit_bullet_list(self, node): + self.list_level +=1 + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + pass + else: + if 'classes' in node and \ + 'auto-toc' in node.attributes['classes']: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('tocenumlist'), + }) + self.list_style_stack.append(self.rststyle('enumitem')) + else: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('tocbulletlist'), + }) + self.list_style_stack.append(self.rststyle('bulletitem')) + self.set_current_element(el) + else: + if self.blockstyle == self.rststyle('blockquote'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('blockquote-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('blockquote-bulletitem')) + elif self.blockstyle == self.rststyle('highlights'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('highlights-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('highlights-bulletitem')) + elif self.blockstyle == self.rststyle('epigraph'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('epigraph-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('epigraph-bulletitem')) + else: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('bulletlist'), + }) + self.list_style_stack.append(self.rststyle('bulletitem')) + self.set_current_element(el) + + def depart_bullet_list(self, node): + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + pass + else: + self.set_to_parent() + self.list_style_stack.pop() + else: + self.set_to_parent() + self.list_style_stack.pop() + self.list_level -=1 + + def visit_caption(self, node): + raise nodes.SkipChildren() + pass + + def depart_caption(self, node): + pass + + def visit_comment(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'office:annotation', attrib={}) + el2 = SubElement(el1, 'dc:creator', attrib={}) + s1 = os.environ.get('USER', '') + el2.text = s1 + el2 = SubElement(el1, 'text:p', attrib={}) + el2.text = node.astext() + + def depart_comment(self, node): + pass + + def visit_compound(self, node): + # The compound directive currently receives no special treatment. + pass + + def depart_compound(self, node): + pass + + def visit_container(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + self.paragraph_style_stack.append(self.rststyle(styles[0])) + + def depart_container(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + self.paragraph_style_stack.pop() + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition_list(self, node): + self.def_list_level +=1 + if self.list_level > 5: + raise RuntimeError( + 'max definition list nesting level exceeded') + + def depart_definition_list(self, node): + self.def_list_level -=1 + + def visit_definition_list_item(self, node): + pass + + def depart_definition_list_item(self, node): + pass + + def visit_term(self, node): + el = self.append_p('deflist-term-%d' % self.def_list_level) + el.text = node.astext() + self.set_current_element(el) + raise nodes.SkipChildren() + + def depart_term(self, node): + self.set_to_parent() + + def visit_definition(self, node): + self.paragraph_style_stack.append( + self.rststyle('deflist-def-%d' % self.def_list_level)) + self.bumped_list_level_stack.append(ListLevel(1)) + + def depart_definition(self, node): + self.paragraph_style_stack.pop() + self.bumped_list_level_stack.pop() + + def visit_classifier(self, node): + els = self.current_element.getchildren() + if len(els) > 0: + el = els[-1] + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('emphasis') + }) + el1.text = ' (%s)' % (node.astext(), ) + + def depart_classifier(self, node): + pass + + def visit_document(self, node): + pass + + def depart_document(self, node): + self.process_footnotes() + + def visit_docinfo(self, node): + self.section_level += 1 + self.section_count += 1 + if self.settings.create_sections: + el = self.append_child('text:section', attrib={ + 'text:name': 'Section%d' % self.section_count, + 'text:style-name': 'Sect%d' % self.section_level, + }) + self.set_current_element(el) + + def depart_docinfo(self, node): + self.section_level -= 1 + if self.settings.create_sections: + self.set_to_parent() + + def visit_emphasis(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('emphasis')}) + self.set_current_element(el) + + def depart_emphasis(self, node): + self.set_to_parent() + + def visit_enumerated_list(self, node): + el1 = self.current_element + if self.blockstyle == self.rststyle('blockquote'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('blockquote-enumlist'), + }) + self.list_style_stack.append(self.rststyle('blockquote-enumitem')) + elif self.blockstyle == self.rststyle('highlights'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('highlights-enumlist'), + }) + self.list_style_stack.append(self.rststyle('highlights-enumitem')) + elif self.blockstyle == self.rststyle('epigraph'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('epigraph-enumlist'), + }) + self.list_style_stack.append(self.rststyle('epigraph-enumitem')) + else: + liststylename = 'enumlist-%s' % (node.get('enumtype', 'arabic'), ) + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle(liststylename), + }) + self.list_style_stack.append(self.rststyle('enumitem')) + self.set_current_element(el2) + + def depart_enumerated_list(self, node): + self.set_to_parent() + self.list_style_stack.pop() + + def visit_list_item(self, node): + # If we are in a "bumped" list level, then wrap this + # list in an outer lists in order to increase the + # indentation level. + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + self.paragraph_style_stack.append( + self.rststyle('contents-%d' % (self.list_level, ))) + else: + el1 = self.append_child('text:list-item') + self.set_current_element(el1) + else: + el1 = self.append_child('text:list-item') + el3 = el1 + if len(self.bumped_list_level_stack) > 0: + level_obj = self.bumped_list_level_stack[-1] + if level_obj.get_sibling(): + level_obj.set_nested(False) + for level_obj1 in self.bumped_list_level_stack: + for idx in range(level_obj1.get_level()): + el2 = self.append_child('text:list', parent=el3) + el3 = self.append_child( + 'text:list-item', parent=el2) + self.paragraph_style_stack.append(self.list_style_stack[-1]) + self.set_current_element(el3) + + def depart_list_item(self, node): + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + self.paragraph_style_stack.pop() + else: + self.set_to_parent() + else: + if len(self.bumped_list_level_stack) > 0: + level_obj = self.bumped_list_level_stack[-1] + if level_obj.get_sibling(): + level_obj.set_nested(True) + for level_obj1 in self.bumped_list_level_stack: + for idx in range(level_obj1.get_level()): + self.set_to_parent() + self.set_to_parent() + self.paragraph_style_stack.pop() + self.set_to_parent() + + def visit_header(self, node): + self.in_header = True + + def depart_header(self, node): + self.in_header = False + + def visit_footer(self, node): + self.in_footer = True + + def depart_footer(self, node): + self.in_footer = False + + def visit_field(self, node): + pass + + def depart_field(self, node): + pass + + def visit_field_list(self, node): + pass + + def depart_field_list(self, node): + pass + + def visit_field_name(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = node.astext() + + def depart_field_name(self, node): + pass + + def visit_field_body(self, node): + self.paragraph_style_stack.append(self.rststyle('blockindent')) + + def depart_field_body(self, node): + self.paragraph_style_stack.pop() + + def visit_figure(self, node): + pass + + def depart_figure(self, node): + pass + + def visit_footnote(self, node): + self.footnote_level += 1 + self.save_footnote_current = self.current_element + el1 = Element('text:note-body') + self.current_element = el1 + self.footnote_list.append((node, el1)) + if isinstance(node, docutils.nodes.citation): + self.paragraph_style_stack.append(self.rststyle('citation')) + else: + self.paragraph_style_stack.append(self.rststyle('footnote')) + + def depart_footnote(self, node): + self.paragraph_style_stack.pop() + self.current_element = self.save_footnote_current + self.footnote_level -= 1 + + footnote_chars = [ + '*', '**', '***', + '++', '+++', + '##', '###', + '@@', '@@@', + ] + + def visit_footnote_reference(self, node): + if self.footnote_level <= 0: + id = node.attributes['ids'][0] + refid = node.attributes.get('refid') + if refid is None: + refid = '' + if self.settings.endnotes_end_doc: + note_class = 'endnote' + else: + note_class = 'footnote' + el1 = self.append_child('text:note', attrib={ + 'text:id': '%s' % (refid, ), + 'text:note-class': note_class, + }) + note_auto = str(node.attributes.get('auto', 1)) + if isinstance(node, docutils.nodes.citation_reference): + citation = '[%s]' % node.astext() + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': citation, + }) + el2.text = citation + elif note_auto == '1': + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': node.astext(), + }) + el2.text = node.astext() + elif note_auto == '*': + if self.footnote_chars_idx >= len( + ODFTranslator.footnote_chars): + self.footnote_chars_idx = 0 + footnote_char = ODFTranslator.footnote_chars[ + self.footnote_chars_idx] + self.footnote_chars_idx += 1 + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': footnote_char, + }) + el2.text = footnote_char + self.footnote_ref_dict[id] = el1 + raise nodes.SkipChildren() + + def depart_footnote_reference(self, node): + pass + + def visit_citation(self, node): + self.in_citation = True + for id in node.attributes['ids']: + self.citation_id = id + break + self.paragraph_style_stack.append(self.rststyle('blockindent')) + self.bumped_list_level_stack.append(ListLevel(1)) + + def depart_citation(self, node): + self.citation_id = None + self.paragraph_style_stack.pop() + self.bumped_list_level_stack.pop() + self.in_citation = False + + def visit_citation_reference(self, node): + if self.settings.create_links: + id = node.attributes['refid'] + el = self.append_child('text:reference-ref', attrib={ + 'text:ref-name': '%s' % (id, ), + 'text:reference-format': 'text', + }) + el.text = '[' + self.set_current_element(el) + elif self.current_element.text is None: + self.current_element.text = '[' + else: + self.current_element.text += '[' + + def depart_citation_reference(self, node): + self.current_element.text += ']' + if self.settings.create_links: + self.set_to_parent() + + def visit_label(self, node): + if isinstance(node.parent, docutils.nodes.footnote): + raise nodes.SkipChildren() + elif self.citation_id is not None: + el = self.append_p('textbody') + self.set_current_element(el) + if self.settings.create_links: + el0 = SubElement(el, 'text:span') + el0.text = '[' + el1 = self.append_child('text:reference-mark-start', attrib={ + 'text:name': '%s' % (self.citation_id, ), + }) + else: + el.text = '[' + + def depart_label(self, node): + if isinstance(node.parent, docutils.nodes.footnote): + pass + elif self.citation_id is not None: + if self.settings.create_links: + el = self.append_child('text:reference-mark-end', attrib={ + 'text:name': '%s' % (self.citation_id, ), + }) + el0 = SubElement(self.current_element, 'text:span') + el0.text = ']' + else: + self.current_element.text += ']' + self.set_to_parent() + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def check_file_exists(self, path): + if os.path.exists(path): + return 1 + else: + return 0 + + def visit_image(self, node): + # Capture the image file. + if 'uri' in node.attributes: + source = node.attributes['uri'] + if not source.startswith('http:'): + if not source.startswith(os.sep): + docsource, line = utils.get_source_line(node) + if docsource: + dirname = os.path.dirname(docsource) + if dirname: + source = '%s%s%s' % (dirname, os.sep, source, ) + if not self.check_file_exists(source): + self.document.reporter.warning( + 'Cannot find image file %s.' % (source, )) + return + else: + return + if source in self.image_dict: + filename, destination = self.image_dict[source] + else: + self.image_count += 1 + filename = os.path.split(source)[1] + destination = 'Pictures/1%08x%s' % (self.image_count, filename, ) + if source.startswith('http:'): + try: + imgfile = urllib.request.urlopen(source) + content = imgfile.read() + imgfile.close() + imgfile2 = tempfile.NamedTemporaryFile('wb', delete=False) + imgfile2.write(content) + imgfile2.close() + imgfilename = imgfile2.name + source = imgfilename + except urllib.error.HTTPError as e: + self.document.reporter.warning( + "Can't open image url %s." % (source, )) + spec = (source, destination,) + else: + spec = (os.path.abspath(source), destination,) + self.embedded_file_list.append(spec) + self.image_dict[source] = (source, destination,) + # Is this a figure (containing an image) or just a plain image? + if self.in_paragraph: + el1 = self.current_element + else: + el1 = SubElement(self.current_element, 'text:p', + attrib={'text:style-name': self.rststyle('textbody')}) + el2 = el1 + if isinstance(node.parent, docutils.nodes.figure): + el3, el4, el5, caption = self.generate_figure(node, source, + destination, el2) + attrib = {} + el6, width = self.generate_image(node, source, destination, + el5, attrib) + if caption is not None: + el6.tail = caption + else: #if isinstance(node.parent, docutils.nodes.image): + el3 = self.generate_image(node, source, destination, el2) + + def depart_image(self, node): + pass + + def get_image_width_height(self, node, attr): + size = None + if attr in node.attributes: + size = node.attributes[attr] + unit = size[-2:] + if unit.isalpha(): + size = size[:-2] + else: + unit = 'px' + try: + size = float(size) + except ValueError as e: + self.document.reporter.warning( + 'Invalid %s for image: "%s"' % ( + attr, node.attributes[attr])) + size = [size, unit] + return size + + def get_image_scale(self, node): + if 'scale' in node.attributes: + try: + scale = int(node.attributes['scale']) + if scale < 1: # or scale > 100: + self.document.reporter.warning( + 'scale out of range (%s), using 1.' % (scale, )) + scale = 1 + scale = scale * 0.01 + except ValueError as e: + self.document.reporter.warning( + 'Invalid scale for image: "%s"' % ( + node.attributes['scale'], )) + else: + scale = 1.0 + return scale + + def get_image_scaled_width_height(self, node, source): + scale = self.get_image_scale(node) + width = self.get_image_width_height(node, 'width') + height = self.get_image_width_height(node, 'height') + + dpi = (72, 72) + if PIL is not None and source in self.image_dict: + filename, destination = self.image_dict[source] + imageobj = PIL.Image.open(filename, 'r') + dpi = imageobj.info.get('dpi', dpi) + # dpi information can be (xdpi, ydpi) or xydpi + try: iter(dpi) + except: dpi = (dpi, dpi) + else: + imageobj = None + + if width is None or height is None: + if imageobj is None: + raise RuntimeError( + 'image size not fully specified and PIL not installed') + if width is None: width = [imageobj.size[0], 'px'] + if height is None: height = [imageobj.size[1], 'px'] + + width[0] *= scale + height[0] *= scale + if width[1] == 'px': width = [width[0] / dpi[0], 'in'] + if height[1] == 'px': height = [height[0] / dpi[1], 'in'] + + width[0] = str(width[0]) + height[0] = str(height[0]) + return ''.join(width), ''.join(height) + + def generate_figure(self, node, source, destination, current_element): + caption = None + width, height = self.get_image_scaled_width_height(node, source) + for node1 in node.parent.children: + if node1.tagname == 'caption': + caption = node1.astext() + self.image_style_count += 1 + # + # Add the style for the caption. + if caption is not None: + attrib = { + 'style:class': 'extra', + 'style:family': 'paragraph', + 'style:name': 'Caption', + 'style:parent-style-name': 'Standard', + } + el1 = SubElement(self.automatic_styles, 'style:style', + attrib=attrib, nsdict=SNSD) + attrib = { + 'fo:margin-bottom': '0.0835in', + 'fo:margin-top': '0.0835in', + 'text:line-number': '0', + 'text:number-lines': 'false', + } + el2 = SubElement(el1, 'style:paragraph-properties', + attrib=attrib, nsdict=SNSD) + attrib = { + 'fo:font-size': '12pt', + 'fo:font-style': 'italic', + 'style:font-name': 'Times', + 'style:font-name-complex': 'Lucidasans1', + 'style:font-size-asian': '12pt', + 'style:font-size-complex': '12pt', + 'style:font-style-asian': 'italic', + 'style:font-style-complex': 'italic', + } + el2 = SubElement(el1, 'style:text-properties', + attrib=attrib, nsdict=SNSD) + style_name = 'rstframestyle%d' % self.image_style_count + # Add the styles + attrib = { + 'style:name': style_name, + 'style:family': 'graphic', + 'style:parent-style-name': self.rststyle('figureframe'), + } + el1 = SubElement(self.automatic_styles, + 'style:style', attrib=attrib, nsdict=SNSD) + halign = 'center' + valign = 'top' + if 'align' in node.attributes: + align = node.attributes['align'].split() + for val in align: + if val in ('left', 'center', 'right'): + halign = val + elif val in ('top', 'middle', 'bottom'): + valign = val + attrib = {} + wrap = False + classes = node.parent.attributes.get('classes') + if classes and 'wrap' in classes: + wrap = True + if wrap: + attrib['style:wrap'] = 'dynamic' + else: + attrib['style:wrap'] = 'none' + el2 = SubElement(el1, + 'style:graphic-properties', attrib=attrib, nsdict=SNSD) + attrib = { + 'draw:style-name': style_name, + 'draw:name': 'Frame1', + 'text:anchor-type': 'paragraph', + 'draw:z-index': '0', + } + attrib['svg:width'] = width + el3 = SubElement(current_element, 'draw:frame', attrib=attrib) + attrib = {} + el4 = SubElement(el3, 'draw:text-box', attrib=attrib) + attrib = { + 'text:style-name': self.rststyle('caption'), + } + el5 = SubElement(el4, 'text:p', attrib=attrib) + return el3, el4, el5, caption + + def generate_image(self, node, source, destination, current_element, + frame_attrs=None): + width, height = self.get_image_scaled_width_height(node, source) + self.image_style_count += 1 + style_name = 'rstframestyle%d' % self.image_style_count + # Add the style. + attrib = { + 'style:name': style_name, + 'style:family': 'graphic', + 'style:parent-style-name': self.rststyle('image'), + } + el1 = SubElement(self.automatic_styles, + 'style:style', attrib=attrib, nsdict=SNSD) + halign = None + valign = None + if 'align' in node.attributes: + align = node.attributes['align'].split() + for val in align: + if val in ('left', 'center', 'right'): + halign = val + elif val in ('top', 'middle', 'bottom'): + valign = val + if frame_attrs is None: + attrib = { + 'style:vertical-pos': 'top', + 'style:vertical-rel': 'paragraph', + 'style:horizontal-rel': 'paragraph', + 'style:mirror': 'none', + 'fo:clip': 'rect(0cm 0cm 0cm 0cm)', + 'draw:luminance': '0%', + 'draw:contrast': '0%', + 'draw:red': '0%', + 'draw:green': '0%', + 'draw:blue': '0%', + 'draw:gamma': '100%', + 'draw:color-inversion': 'false', + 'draw:image-opacity': '100%', + 'draw:color-mode': 'standard', + } + else: + attrib = frame_attrs + if halign is not None: + attrib['style:horizontal-pos'] = halign + if valign is not None: + attrib['style:vertical-pos'] = valign + # If there is a classes/wrap directive or we are + # inside a table, add a no-wrap style. + wrap = False + classes = node.attributes.get('classes') + if classes and 'wrap' in classes: + wrap = True + if wrap: + attrib['style:wrap'] = 'dynamic' + else: + attrib['style:wrap'] = 'none' + # If we are inside a table, add a no-wrap style. + if self.is_in_table(node): + attrib['style:wrap'] = 'none' + el2 = SubElement(el1, + 'style:graphic-properties', attrib=attrib, nsdict=SNSD) + # Add the content. + #el = SubElement(current_element, 'text:p', + # attrib={'text:style-name': self.rststyle('textbody')}) + attrib={ + 'draw:style-name': style_name, + 'draw:name': 'graphics2', + 'draw:z-index': '1', + } + if isinstance(node.parent, nodes.TextElement): + attrib['text:anchor-type'] = 'as-char' #vds + else: + attrib['text:anchor-type'] = 'paragraph' + attrib['svg:width'] = width + attrib['svg:height'] = height + el1 = SubElement(current_element, 'draw:frame', attrib=attrib) + el2 = SubElement(el1, 'draw:image', attrib={ + 'xlink:href': '%s' % (destination, ), + 'xlink:type': 'simple', + 'xlink:show': 'embed', + 'xlink:actuate': 'onLoad', + }) + return el1, width + + def is_in_table(self, node): + node1 = node.parent + while node1: + if isinstance(node1, docutils.nodes.entry): + return True + node1 = node1.parent + return False + + def visit_legend(self, node): + if isinstance(node.parent, docutils.nodes.figure): + el1 = self.current_element[-1] + el1 = el1[0][0] + self.current_element = el1 + self.paragraph_style_stack.append(self.rststyle('legend')) + + def depart_legend(self, node): + if isinstance(node.parent, docutils.nodes.figure): + self.paragraph_style_stack.pop() + self.set_to_parent() + self.set_to_parent() + self.set_to_parent() + + def visit_line_block(self, node): + self.line_indent_level += 1 + self.line_block_level += 1 + + def depart_line_block(self, node): + self.line_indent_level -= 1 + self.line_block_level -= 1 + + def visit_line(self, node): + style = 'lineblock%d' % self.line_indent_level + el1 = SubElement(self.current_element, 'text:p', attrib={ + 'text:style-name': self.rststyle(style), + }) + self.current_element = el1 + + def depart_line(self, node): + self.set_to_parent() + + def visit_literal(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('inlineliteral')}) + self.set_current_element(el) + + def depart_literal(self, node): + self.set_to_parent() + + def visit_inline(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + inline_style = styles[0] + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle(inline_style)}) + self.set_current_element(el) + + def depart_inline(self, node): + self.set_to_parent() + + def _calculate_code_block_padding(self, line): + count = 0 + matchobj = SPACES_PATTERN.match(line) + if matchobj: + pad = matchobj.group() + count = len(pad) + else: + matchobj = TABS_PATTERN.match(line) + if matchobj: + pad = matchobj.group() + count = len(pad) * 8 + return count + + def _add_syntax_highlighting(self, insource, language): + lexer = pygments.lexers.get_lexer_by_name(language, stripall=True) + if language in ('latex', 'tex'): + fmtr = OdtPygmentsLaTeXFormatter(lambda name, parameters=(): + self.rststyle(name, parameters), + escape_function=escape_cdata) + else: + fmtr = OdtPygmentsProgFormatter(lambda name, parameters=(): + self.rststyle(name, parameters), + escape_function=escape_cdata) + outsource = pygments.highlight(insource, lexer, fmtr) + return outsource + + def fill_line(self, line): + line = FILL_PAT1.sub(self.fill_func1, line) + line = FILL_PAT2.sub(self.fill_func2, line) + return line + + def fill_func1(self, matchobj): + spaces = matchobj.group(0) + repl = '' % (len(spaces), ) + return repl + + def fill_func2(self, matchobj): + spaces = matchobj.group(0) + repl = ' ' % (len(spaces) - 1, ) + return repl + + def visit_literal_block(self, node): + if len(self.paragraph_style_stack) > 1: + wrapper1 = '%%s' % ( + self.rststyle('codeblock-indented'), ) + else: + wrapper1 = '%%s' % ( + self.rststyle('codeblock'), ) + source = node.astext() + if (pygments and + self.settings.add_syntax_highlighting + #and + #node.get('hilight', False) + ): + language = node.get('language', 'python') + source = self._add_syntax_highlighting(source, language) + else: + source = escape_cdata(source) + lines = source.split('\n') + # If there is an empty last line, remove it. + if lines[-1] == '': + del lines[-1] + lines1 = [''] + + my_lines = [] + for my_line in lines: + my_line = self.fill_line(my_line) + my_line = my_line.replace(" ", "\n") + my_lines.append(my_line) + my_lines_str = ''.join(my_lines) + my_lines_str2 = wrapper1 % (my_lines_str, ) + lines1.append(my_lines_str2) + lines1.append('') + s1 = ''.join(lines1) + if WhichElementTree != "lxml": + s1 = s1.encode("utf-8") + el1 = etree.fromstring(s1) + children = el1.getchildren() + for child in children: + self.current_element.append(child) + + def depart_literal_block(self, node): + pass + + visit_doctest_block = visit_literal_block + depart_doctest_block = depart_literal_block + + # placeholder for math (see docs/dev/todo.txt) + def visit_math(self, node): + self.document.reporter.warning('"math" role not supported', + base_node=node) + self.visit_literal(node) + + def depart_math(self, node): + self.depart_literal(node) + + def visit_math_block(self, node): + self.document.reporter.warning('"math" directive not supported', + base_node=node) + self.visit_literal_block(node) + + def depart_math_block(self, node): + self.depart_literal_block(node) + + def visit_meta(self, node): + name = node.attributes.get('name') + content = node.attributes.get('content') + if name is not None and content is not None: + self.meta_dict[name] = content + + def depart_meta(self, node): + pass + + def visit_option_list(self, node): + table_name = 'tableoption' + # + # Generate automatic styles + if not self.optiontablestyles_generated: + self.optiontablestyles_generated = True + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle(table_name), + 'style:family': 'table'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-properties', attrib={ + 'style:width': '17.59cm', + 'table:align': 'left', + 'style:shadow': 'none'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle('%s.%%c' % table_name, ( 'A', )), + 'style:family': 'table-column'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-column-properties', attrib={ + 'style:column-width': '4.999cm'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle('%s.%%c' % table_name, ( 'B', )), + 'style:family': 'table-column'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-column-properties', attrib={ + 'style:column-width': '12.587cm'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 1, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:background-color': 'transparent', + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': 'none', + 'fo:border-top': '0.035cm solid #000000', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + el2 = SubElement(el1, 'style:background-image', nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 1, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border': '0.035cm solid #000000'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 2, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': 'none', + 'fo:border-top': 'none', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 2, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': '0.035cm solid #000000', + 'fo:border-top': 'none', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + # + # Generate table data + el = self.append_child('table:table', attrib={ + 'table:name': self.rststyle(table_name), + 'table:style-name': self.rststyle(table_name), + }) + el1 = SubElement(el, 'table:table-column', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c' % table_name, ( 'A', ))}) + el1 = SubElement(el, 'table:table-column', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c' % table_name, ( 'B', ))}) + el1 = SubElement(el, 'table:table-header-rows') + el2 = SubElement(el1, 'table:table-row') + el3 = SubElement(el2, 'table:table-cell', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 1, )), + 'office:value-type': 'string'}) + el4 = SubElement(el3, 'text:p', attrib={ + 'text:style-name': 'Table_20_Heading'}) + el4.text= 'Option' + el3 = SubElement(el2, 'table:table-cell', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 1, )), + 'office:value-type': 'string'}) + el4 = SubElement(el3, 'text:p', attrib={ + 'text:style-name': 'Table_20_Heading'}) + el4.text= 'Description' + self.set_current_element(el) + + def depart_option_list(self, node): + self.set_to_parent() + + def visit_option_list_item(self, node): + el = self.append_child('table:table-row') + self.set_current_element(el) + + def depart_option_list_item(self, node): + self.set_to_parent() + + def visit_option_group(self, node): + el = self.append_child('table:table-cell', attrib={ + 'table:style-name': 'Table%d.A2' % self.table_count, + 'office:value-type': 'string', + }) + self.set_current_element(el) + + def depart_option_group(self, node): + self.set_to_parent() + + def visit_option(self, node): + el = self.append_child('text:p', attrib={ + 'text:style-name': 'Table_20_Contents'}) + el.text = node.astext() + + def depart_option(self, node): + pass + + def visit_option_string(self, node): + pass + + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + pass + + def depart_option_argument(self, node): + pass + + def visit_description(self, node): + el = self.append_child('table:table-cell', attrib={ + 'table:style-name': 'Table%d.B2' % self.table_count, + 'office:value-type': 'string', + }) + el1 = SubElement(el, 'text:p', attrib={ + 'text:style-name': 'Table_20_Contents'}) + el1.text = node.astext() + raise nodes.SkipChildren() + + def depart_description(self, node): + pass + + def visit_paragraph(self, node): + self.in_paragraph = True + if self.in_header: + el = self.append_p('header') + elif self.in_footer: + el = self.append_p('footer') + else: + style_name = self.paragraph_style_stack[-1] + el = self.append_child('text:p', + attrib={'text:style-name': style_name}) + self.append_pending_ids(el) + self.set_current_element(el) + + def depart_paragraph(self, node): + self.in_paragraph = False + self.set_to_parent() + if self.in_header: + self.header_content.append( + self.current_element.getchildren()[-1]) + self.current_element.remove( + self.current_element.getchildren()[-1]) + elif self.in_footer: + self.footer_content.append( + self.current_element.getchildren()[-1]) + self.current_element.remove( + self.current_element.getchildren()[-1]) + + def visit_problematic(self, node): + pass + + def depart_problematic(self, node): + pass + + def visit_raw(self, node): + if 'format' in node.attributes: + formats = node.attributes['format'] + formatlist = formats.split() + if 'odt' in formatlist: + rawstr = node.astext() + attrstr = ' '.join(['%s="%s"' % (k, v, ) + for k,v in list(CONTENT_NAMESPACE_ATTRIB.items())]) + contentstr = '%s' % (attrstr, rawstr, ) + if WhichElementTree != "lxml": + contentstr = contentstr.encode("utf-8") + content = etree.fromstring(contentstr) + elements = content.getchildren() + if len(elements) > 0: + el1 = elements[0] + if self.in_header: + pass + elif self.in_footer: + pass + else: + self.current_element.append(el1) + raise nodes.SkipChildren() + + def depart_raw(self, node): + if self.in_header: + pass + elif self.in_footer: + pass + else: + pass + + def visit_reference(self, node): + text = node.astext() + if self.settings.create_links: + if 'refuri' in node: + href = node['refuri'] + if ( self.settings.cloak_email_addresses + and href.startswith('mailto:')): + href = self.cloak_mailto(href) + el = self.append_child('text:a', attrib={ + 'xlink:href': '%s' % href, + 'xlink:type': 'simple', + }) + self.set_current_element(el) + elif 'refid' in node: + if self.settings.create_links: + href = node['refid'] + el = self.append_child('text:reference-ref', attrib={ + 'text:ref-name': '%s' % href, + 'text:reference-format': 'text', + }) + else: + self.document.reporter.warning( + 'References must have "refuri" or "refid" attribute.') + if (self.in_table_of_contents and + len(node.children) >= 1 and + isinstance(node.children[0], docutils.nodes.generated)): + node.remove(node.children[0]) + + def depart_reference(self, node): + if self.settings.create_links: + if 'refuri' in node: + self.set_to_parent() + + def visit_rubric(self, node): + style_name = self.rststyle('rubric') + classes = node.get('classes') + if classes: + class1 = classes[0] + if class1: + style_name = class1 + el = SubElement(self.current_element, 'text:h', attrib = { + #'text:outline-level': '%d' % section_level, + #'text:style-name': 'Heading_20_%d' % section_level, + 'text:style-name': style_name, + }) + text = node.astext() + el.text = self.encode(text) + + def depart_rubric(self, node): + pass + + def visit_section(self, node, move_ids=1): + self.section_level += 1 + self.section_count += 1 + if self.settings.create_sections: + el = self.append_child('text:section', attrib={ + 'text:name': 'Section%d' % self.section_count, + 'text:style-name': 'Sect%d' % self.section_level, + }) + self.set_current_element(el) + + def depart_section(self, node): + self.section_level -= 1 + if self.settings.create_sections: + self.set_to_parent() + + def visit_strong(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + self.set_current_element(el) + + def depart_strong(self, node): + self.set_to_parent() + + def visit_substitution_definition(self, node): + raise nodes.SkipChildren() + + def depart_substitution_definition(self, node): + pass + + def visit_system_message(self, node): + pass + + def depart_system_message(self, node): + pass + + def get_table_style(self, node): + table_style = None + table_name = None + use_predefined_table_style = False + str_classes = node.get('classes') + if str_classes is not None: + for str_class in str_classes: + if str_class.startswith(TABLESTYLEPREFIX): + table_name = str_class + use_predefined_table_style = True + break + if table_name is not None: + table_style = self.table_styles.get(table_name) + if table_style is None: + # If we can't find the table style, issue warning + # and use the default table style. + self.document.reporter.warning( + 'Can\'t find table style "%s". Using default.' % ( + table_name, )) + table_name = TABLENAMEDEFAULT + table_style = self.table_styles.get(table_name) + if table_style is None: + # If we can't find the default table style, issue a warning + # and use a built-in default style. + self.document.reporter.warning( + 'Can\'t find default table style "%s". Using built-in default.' % ( + table_name, )) + table_style = BUILTIN_DEFAULT_TABLE_STYLE + else: + table_name = TABLENAMEDEFAULT + table_style = self.table_styles.get(table_name) + if table_style is None: + # If we can't find the default table style, issue a warning + # and use a built-in default style. + self.document.reporter.warning( + 'Can\'t find default table style "%s". Using built-in default.' % ( + table_name, )) + table_style = BUILTIN_DEFAULT_TABLE_STYLE + return table_style + + def visit_table(self, node): + self.table_count += 1 + table_style = self.get_table_style(node) + table_name = '%s%%d' % TABLESTYLEPREFIX + el1 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + 'style:family': 'table', + }, nsdict=SNSD) + if table_style.backgroundcolor is None: + el1_1 = SubElement(el1, 'style:table-properties', attrib={ + #'style:width': '17.59cm', + #'table:align': 'margins', + 'table:align': 'left', + 'fo:margin-top': '0in', + 'fo:margin-bottom': '0.10in', + }, nsdict=SNSD) + else: + el1_1 = SubElement(el1, 'style:table-properties', attrib={ + #'style:width': '17.59cm', + 'table:align': 'margins', + 'fo:margin-top': '0in', + 'fo:margin-bottom': '0.10in', + 'fo:background-color': table_style.backgroundcolor, + }, nsdict=SNSD) + # We use a single cell style for all cells in this table. + # That's probably not correct, but seems to work. + el2 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( self.table_count, 'A', 1, )), + 'style:family': 'table-cell', + }, nsdict=SNSD) + thickness = self.settings.table_border_thickness + if thickness is None: + line_style1 = table_style.border + else: + line_style1 = '0.%03dcm solid #000000' % (thickness, ) + el2_1 = SubElement(el2, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.049cm', + 'fo:border-left': line_style1, + 'fo:border-right': line_style1, + 'fo:border-top': line_style1, + 'fo:border-bottom': line_style1, + }, nsdict=SNSD) + title = None + for child in node.children: + if child.tagname == 'title': + title = child.astext() + break + if title is not None: + el3 = self.append_p('table-title', title) + else: + pass + el4 = SubElement(self.current_element, 'table:table', attrib={ + 'table:name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + 'table:style-name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + }) + self.set_current_element(el4) + self.current_table_style = el1 + self.table_width = 0.0 + + def depart_table(self, node): + attribkey = add_ns('style:width', nsdict=SNSD) + attribval = '%.4fin' % (self.table_width, ) + el1 = self.current_table_style + el2 = el1[0] + el2.attrib[attribkey] = attribval + self.set_to_parent() + + def visit_tgroup(self, node): + self.column_count = ord('A') - 1 + + def depart_tgroup(self, node): + pass + + def visit_colspec(self, node): + self.column_count += 1 + colspec_name = self.rststyle( + '%s%%d.%%s' % TABLESTYLEPREFIX, + (self.table_count, chr(self.column_count), ) + ) + colwidth = node['colwidth'] / 12.0 + el1 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': colspec_name, + 'style:family': 'table-column', + }, nsdict=SNSD) + el1_1 = SubElement(el1, 'style:table-column-properties', attrib={ + 'style:column-width': '%.4fin' % colwidth + }, + nsdict=SNSD) + el2 = self.append_child('table:table-column', attrib={ + 'table:style-name': colspec_name, + }) + self.table_width += colwidth + + def depart_colspec(self, node): + pass + + def visit_thead(self, node): + el = self.append_child('table:table-header-rows') + self.set_current_element(el) + self.in_thead = True + self.paragraph_style_stack.append('Table_20_Heading') + + def depart_thead(self, node): + self.set_to_parent() + self.in_thead = False + self.paragraph_style_stack.pop() + + def visit_row(self, node): + self.column_count = ord('A') - 1 + el = self.append_child('table:table-row') + self.set_current_element(el) + + def depart_row(self, node): + self.set_to_parent() + + def visit_entry(self, node): + self.column_count += 1 + cellspec_name = self.rststyle( + '%s%%d.%%c%%d' % TABLESTYLEPREFIX, + (self.table_count, 'A', 1, ) + ) + attrib={ + 'table:style-name': cellspec_name, + 'office:value-type': 'string', + } + morecols = node.get('morecols', 0) + if morecols > 0: + attrib['table:number-columns-spanned'] = '%d' % (morecols + 1,) + self.column_count += morecols + morerows = node.get('morerows', 0) + if morerows > 0: + attrib['table:number-rows-spanned'] = '%d' % (morerows + 1,) + el1 = self.append_child('table:table-cell', attrib=attrib) + self.set_current_element(el1) + + def depart_entry(self, node): + self.set_to_parent() + + def visit_tbody(self, node): + pass + + def depart_tbody(self, node): + pass + + def visit_target(self, node): + # + # I don't know how to implement targets in ODF. + # How do we create a target in oowriter? A cross-reference? + if not ('refuri' in node or 'refid' in node + or 'refname' in node): + pass + else: + pass + + def depart_target(self, node): + pass + + def visit_title(self, node, move_ids=1, title_type='title'): + if isinstance(node.parent, docutils.nodes.section): + section_level = self.section_level + if section_level > 7: + self.document.reporter.warning( + 'Heading/section levels greater than 7 not supported.') + self.document.reporter.warning( + ' Reducing to heading level 7 for heading: "%s"' % ( + node.astext(), )) + section_level = 7 + el1 = self.append_child('text:h', attrib = { + 'text:outline-level': '%d' % section_level, + #'text:style-name': 'Heading_20_%d' % section_level, + 'text:style-name': self.rststyle( + 'heading%d', (section_level, )), + }) + self.append_pending_ids(el1) + self.set_current_element(el1) + elif isinstance(node.parent, docutils.nodes.document): + # text = self.settings.title + #else: + # text = node.astext() + el1 = SubElement(self.current_element, 'text:p', attrib = { + 'text:style-name': self.rststyle(title_type), + }) + self.append_pending_ids(el1) + text = node.astext() + self.title = text + self.found_doc_title = True + self.set_current_element(el1) + + def depart_title(self, node): + if (isinstance(node.parent, docutils.nodes.section) or + isinstance(node.parent, docutils.nodes.document)): + self.set_to_parent() + + def visit_subtitle(self, node, move_ids=1): + self.visit_title(node, move_ids, title_type='subtitle') + + def depart_subtitle(self, node): + self.depart_title(node) + + def visit_title_reference(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': self.rststyle('quotation')}) + el.text = self.encode(node.astext()) + raise nodes.SkipChildren() + + def depart_title_reference(self, node): + pass + + def generate_table_of_content_entry_template(self, el1): + for idx in range(1, 11): + el2 = SubElement(el1, + 'text:table-of-content-entry-template', + attrib={ + 'text:outline-level': "%d" % (idx, ), + 'text:style-name': self.rststyle('contents-%d' % (idx, )), + }) + el3 = SubElement(el2, 'text:index-entry-chapter') + el3 = SubElement(el2, 'text:index-entry-text') + el3 = SubElement(el2, 'text:index-entry-tab-stop', attrib={ + 'style:leader-char': ".", + 'style:type': "right", + }) + el3 = SubElement(el2, 'text:index-entry-page-number') + + def find_title_label(self, node, class_type, label_key): + label = '' + title_node = None + for child in node.children: + if isinstance(child, class_type): + title_node = child + break + if title_node is not None: + label = title_node.astext() + else: + label = self.language.labels[label_key] + return label + + def visit_topic(self, node): + if 'classes' in node.attributes: + if 'contents' in node.attributes['classes']: + label = self.find_title_label(node, docutils.nodes.title, + 'contents') + if self.settings.generate_oowriter_toc: + el1 = self.append_child('text:table-of-content', attrib={ + 'text:name': 'Table of Contents1', + 'text:protected': 'true', + 'text:style-name': 'Sect1', + }) + el2 = SubElement(el1, + 'text:table-of-content-source', + attrib={ + 'text:outline-level': '10', + }) + el3 =SubElement(el2, 'text:index-title-template', attrib={ + 'text:style-name': 'Contents_20_Heading', + }) + el3.text = label + self.generate_table_of_content_entry_template(el2) + el4 = SubElement(el1, 'text:index-body') + el5 = SubElement(el4, 'text:index-title') + el6 = SubElement(el5, 'text:p', attrib={ + 'text:style-name': self.rststyle('contents-heading'), + }) + el6.text = label + self.save_current_element = self.current_element + self.table_of_content_index_body = el4 + self.set_current_element(el4) + else: + el = self.append_p('horizontalline') + el = self.append_p('centeredtextbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + self.in_table_of_contents = True + elif 'abstract' in node.attributes['classes']: + el = self.append_p('horizontalline') + el = self.append_p('centeredtextbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + label = self.find_title_label(node, docutils.nodes.title, + 'abstract') + el1.text = label + elif 'dedication' in node.attributes['classes']: + el = self.append_p('horizontalline') + el = self.append_p('centeredtextbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + label = self.find_title_label(node, docutils.nodes.title, + 'dedication') + el1.text = label + + def depart_topic(self, node): + if 'classes' in node.attributes: + if 'contents' in node.attributes['classes']: + if self.settings.generate_oowriter_toc: + self.update_toc_page_numbers( + self.table_of_content_index_body) + self.set_current_element(self.save_current_element) + else: + el = self.append_p('horizontalline') + self.in_table_of_contents = False + + def update_toc_page_numbers(self, el): + collection = [] + self.update_toc_collect(el, 0, collection) + self.update_toc_add_numbers(collection) + + def update_toc_collect(self, el, level, collection): + collection.append((level, el)) + level += 1 + for child_el in el.getchildren(): + if child_el.tag != 'text:index-body': + self.update_toc_collect(child_el, level, collection) + + def update_toc_add_numbers(self, collection): + for level, el1 in collection: + if (el1.tag == 'text:p' and + el1.text != 'Table of Contents'): + el2 = SubElement(el1, 'text:tab') + el2.tail = '9999' + + + def visit_transition(self, node): + el = self.append_p('horizontalline') + + def depart_transition(self, node): + pass + + # + # Admonitions + # + def visit_warning(self, node): + self.generate_admonition(node, 'warning') + + def depart_warning(self, node): + self.paragraph_style_stack.pop() + + def visit_attention(self, node): + self.generate_admonition(node, 'attention') + + depart_attention = depart_warning + + def visit_caution(self, node): + self.generate_admonition(node, 'caution') + + depart_caution = depart_warning + + def visit_danger(self, node): + self.generate_admonition(node, 'danger') + + depart_danger = depart_warning + + def visit_error(self, node): + self.generate_admonition(node, 'error') + + depart_error = depart_warning + + def visit_hint(self, node): + self.generate_admonition(node, 'hint') + + depart_hint = depart_warning + + def visit_important(self, node): + self.generate_admonition(node, 'important') + + depart_important = depart_warning + + def visit_note(self, node): + self.generate_admonition(node, 'note') + + depart_note = depart_warning + + def visit_tip(self, node): + self.generate_admonition(node, 'tip') + + depart_tip = depart_warning + + def visit_admonition(self, node): + title = None + for child in node.children: + if child.tagname == 'title': + title = child.astext() + if title is None: + classes1 = node.get('classes') + if classes1: + title = classes1[0] + self.generate_admonition(node, 'generic', title) + + depart_admonition = depart_warning + + def generate_admonition(self, node, label, title=None): + el1 = SubElement(self.current_element, 'text:p', attrib = { + 'text:style-name': self.rststyle('admon-%s-hdr', ( label, )), + }) + if title: + el1.text = title + else: + el1.text = '%s!' % (label.capitalize(), ) + s1 = self.rststyle('admon-%s-body', ( label, )) + self.paragraph_style_stack.append(s1) + + # + # Roles (e.g. subscript, superscript, strong, ... + # + def visit_subscript(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': 'rststyle-subscript', + }) + self.set_current_element(el) + + def depart_subscript(self, node): + self.set_to_parent() + + def visit_superscript(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': 'rststyle-superscript', + }) + self.set_current_element(el) + + def depart_superscript(self, node): + self.set_to_parent() + + +# Use an own reader to modify transformations done. +class Reader(standalone.Reader): + + def get_transforms(self): + default = standalone.Reader.get_transforms(self) + if self.settings.create_links: + return default + return [ i + for i in default + if i is not references.DanglingReferences ] diff --git a/python/helpers/py3only/docutils/writers/odf_odt/pygmentsformatter.py b/python/helpers/py3only/docutils/writers/odf_odt/pygmentsformatter.py new file mode 100644 index 000000000000..e8ce8279e5c0 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/odf_odt/pygmentsformatter.py @@ -0,0 +1,109 @@ +# $Id: pygmentsformatter.py 5853 2009-01-19 21:02:02Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" + +Additional support for Pygments formatter. + +""" + + +import pygments +import pygments.formatter + + +class OdtPygmentsFormatter(pygments.formatter.Formatter): + def __init__(self, rststyle_function, escape_function): + pygments.formatter.Formatter.__init__(self) + self.rststyle_function = rststyle_function + self.escape_function = escape_function + + def rststyle(self, name, parameters=( )): + return self.rststyle_function(name, parameters) + + +class OdtPygmentsProgFormatter(OdtPygmentsFormatter): + def format(self, tokensource, outfile): + tokenclass = pygments.token.Token + for ttype, value in tokensource: + value = self.escape_function(value) + if ttype == tokenclass.Keyword: + s2 = self.rststyle('codeblock-keyword') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Literal.String: + s2 = self.rststyle('codeblock-string') + s1 = '%s' % \ + (s2, value, ) + elif ttype in ( + tokenclass.Literal.Number.Integer, + tokenclass.Literal.Number.Integer.Long, + tokenclass.Literal.Number.Float, + tokenclass.Literal.Number.Hex, + tokenclass.Literal.Number.Oct, + tokenclass.Literal.Number, + ): + s2 = self.rststyle('codeblock-number') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Operator: + s2 = self.rststyle('codeblock-operator') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Comment: + s2 = self.rststyle('codeblock-comment') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Class: + s2 = self.rststyle('codeblock-classname') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Function: + s2 = self.rststyle('codeblock-functionname') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name: + s2 = self.rststyle('codeblock-name') + s1 = '%s' % \ + (s2, value, ) + else: + s1 = value + outfile.write(s1) + + +class OdtPygmentsLaTeXFormatter(OdtPygmentsFormatter): + def format(self, tokensource, outfile): + tokenclass = pygments.token.Token + for ttype, value in tokensource: + value = self.escape_function(value) + if ttype == tokenclass.Keyword: + s2 = self.rststyle('codeblock-keyword') + s1 = '%s' % \ + (s2, value, ) + elif ttype in (tokenclass.Literal.String, + tokenclass.Literal.String.Backtick, + ): + s2 = self.rststyle('codeblock-string') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Attribute: + s2 = self.rststyle('codeblock-operator') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Comment: + if value[-1] == '\n': + s2 = self.rststyle('codeblock-comment') + s1 = '%s\n' % \ + (s2, value[:-1], ) + else: + s2 = self.rststyle('codeblock-comment') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Builtin: + s2 = self.rststyle('codeblock-name') + s1 = '%s' % \ + (s2, value, ) + else: + s1 = value + outfile.write(s1) diff --git a/python/helpers/py3only/docutils/writers/odf_odt/styles.odt b/python/helpers/py3only/docutils/writers/odf_odt/styles.odt new file mode 100644 index 000000000000..e17b00721568 Binary files /dev/null and b/python/helpers/py3only/docutils/writers/odf_odt/styles.odt differ diff --git a/python/helpers/py3only/docutils/writers/pep_html/__init__.py b/python/helpers/py3only/docutils/writers/pep_html/__init__.py new file mode 100644 index 000000000000..e4be69ad6b1c --- /dev/null +++ b/python/helpers/py3only/docutils/writers/pep_html/__init__.py @@ -0,0 +1,101 @@ +# $Id: __init__.py 7630 2013-03-15 22:27:04Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +PEP HTML Writer. +""" + +__docformat__ = 'reStructuredText' + +import os +import os.path + +from docutils import frontend, nodes, utils +from docutils.writers import html4css1 + + +class Writer(html4css1.Writer): + + default_stylesheet = 'pep.css' + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + default_template = 'template.txt' + + default_template_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_template)) + + settings_spec = html4css1.Writer.settings_spec + ( + 'PEP/HTML-Specific Options', + 'For the PEP/HTML writer, the default value for the --stylesheet-path ' + 'option is "%s", and the default value for --template is "%s". ' + 'See HTML-Specific Options above.' + % (default_stylesheet_path, default_template_path), + (('Python\'s home URL. Default is "http://www.python.org".', + ['--python-home'], + {'default': 'http://www.python.org', 'metavar': ''}), + ('Home URL prefix for PEPs. Default is "." (current directory).', + ['--pep-home'], + {'default': '.', 'metavar': ''}), + # For testing. + (frontend.SUPPRESS_HELP, + ['--no-random'], + {'action': 'store_true', 'validator': frontend.validate_boolean}),)) + + settings_default_overrides = {'stylesheet_path': default_stylesheet_path, + 'template': default_template_path,} + + relative_path_settings = ('template',) + + config_section = 'pep_html writer' + config_section_dependencies = ('writers', 'html4css1 writer') + + def __init__(self): + html4css1.Writer.__init__(self) + self.translator_class = HTMLTranslator + + def interpolation_dict(self): + subs = html4css1.Writer.interpolation_dict(self) + settings = self.document.settings + pyhome = settings.python_home + subs['pyhome'] = pyhome + subs['pephome'] = settings.pep_home + if pyhome == '..': + subs['pepindex'] = '.' + else: + subs['pepindex'] = pyhome + '/dev/peps' + index = self.document.first_child_matching_class(nodes.field_list) + header = self.document[index] + self.pepnum = header[0][1].astext() + subs['pep'] = self.pepnum + if settings.no_random: + subs['banner'] = 0 + else: + import random + subs['banner'] = random.randrange(64) + try: + subs['pepnum'] = '%04i' % int(self.pepnum) + except ValueError: + subs['pepnum'] = self.pepnum + self.title = header[1][1].astext() + subs['title'] = self.title + subs['body'] = ''.join( + self.body_pre_docinfo + self.docinfo + self.body) + return subs + + def assemble_parts(self): + html4css1.Writer.assemble_parts(self) + self.parts['title'] = [self.title] + self.parts['pepnum'] = self.pepnum + + +class HTMLTranslator(html4css1.HTMLTranslator): + + def depart_field_list(self, node): + html4css1.HTMLTranslator.depart_field_list(self, node) + if 'rfc2822' in node['classes']: + self.body.append('
\n') diff --git a/python/helpers/py3only/docutils/writers/pep_html/pep.css b/python/helpers/py3only/docutils/writers/pep_html/pep.css new file mode 100644 index 000000000000..fc2715f3f764 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/pep_html/pep.css @@ -0,0 +1,344 @@ +/* +:Author: David Goodger +:Contact: goodger@python.org +:date: $Date: 2006-05-21 22:44:42 +0200 (Son, 21. Mai 2006) $ +:version: $Revision: 4564 $ +:copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the PEP HTML output of Docutils. +*/ + +/* "! important" is used here to override other ``margin-top`` and + ``margin-bottom`` styles that are later in the stylesheet or + more specific. See http://www.w3.org/TR/CSS1#the-cascade */ +.first { + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +.navigation { + width: 100% ; + background: #99ccff ; + margin-top: 0px ; + margin-bottom: 0px } + +.navigation .navicon { + width: 150px ; + height: 35px } + +.navigation .textlinks { + padding-left: 1em ; + text-align: left } + +.navigation td, .navigation th { + padding-left: 0em ; + padding-right: 0em ; + vertical-align: middle } + +.rfc2822 { + margin-top: 0.5em ; + margin-left: 0.5em ; + margin-right: 0.5em ; + margin-bottom: 0em } + +.rfc2822 td { + text-align: left } + +.rfc2822 th.field-name { + text-align: right ; + font-family: sans-serif ; + padding-right: 0.5em ; + font-weight: bold ; + margin-bottom: 0em } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +body { + margin: 0px ; + margin-bottom: 1em ; + padding: 0px } + +dl.docutils dd { + margin-bottom: 0.5em } + +div.section { + margin-left: 1em ; + margin-right: 1em ; + margin-bottom: 1.5em } + +div.section div.section { + margin-left: 0em ; + margin-right: 0em ; + margin-top: 1.5em } + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.footer { + margin-left: 1em ; + margin-right: 1em } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin-left: 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1 { + font-family: sans-serif ; + font-size: large } + +h2 { + font-family: sans-serif ; + font-size: medium } + +h3 { + font-family: sans-serif ; + font-size: small } + +h4 { + font-family: sans-serif ; + font-style: italic ; + font-size: small } + +h5 { + font-family: sans-serif; + font-size: x-small } + +h6 { + font-family: sans-serif; + font-style: italic ; + font-size: x-small } + +hr.docutils { + width: 75% } + +img.align-left { + clear: left } + +img.align-right { + clear: right } + +img.borderless { + border: 0 } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-family: sans-serif ; + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.option-argument { + font-style: italic } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +td.num { + text-align: right } + +th.field-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } diff --git a/python/helpers/py3only/docutils/writers/pep_html/template.txt b/python/helpers/py3only/docutils/writers/pep_html/template.txt new file mode 100644 index 000000000000..62c07a87f1f1 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/pep_html/template.txt @@ -0,0 +1,29 @@ + + + + + + + + PEP %(pep)s -- %(title)s + %(stylesheet)s + + + + + +
+%(body)s +%(body_suffix)s diff --git a/python/helpers/py3only/docutils/writers/pseudoxml.py b/python/helpers/py3only/docutils/writers/pseudoxml.py new file mode 100644 index 000000000000..bb833d668e80 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/pseudoxml.py @@ -0,0 +1,31 @@ +# $Id: pseudoxml.py 7320 2012-01-19 22:33:02Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Simple internal document tree Writer, writes indented pseudo-XML. +""" + +__docformat__ = 'reStructuredText' + + +from docutils import writers + + +class Writer(writers.Writer): + + supported = ('pprint', 'pformat', 'pseudoxml') + """Formats this writer supports.""" + + config_section = 'pseudoxml writer' + config_section_dependencies = ('writers',) + + output = None + """Final translated form of `document`.""" + + def translate(self): + self.output = self.document.pformat() + + def supports(self, format): + """This writer supports all format-specific elements.""" + return True diff --git a/python/helpers/py3only/docutils/writers/s5_html/__init__.py b/python/helpers/py3only/docutils/writers/s5_html/__init__.py new file mode 100644 index 000000000000..327c37eb0887 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/__init__.py @@ -0,0 +1,352 @@ +# $Id: __init__.py 7720 2013-09-05 12:54:56Z milde $ +# Authors: Chris Liechti ; +# David Goodger +# Copyright: This module has been placed in the public domain. + +""" +S5/HTML Slideshow Writer. +""" + +__docformat__ = 'reStructuredText' + +import os +import re +import sys + +import docutils +from docutils import frontend, nodes, utils +from docutils._compat import b +from docutils.writers import html4css1 + +themes_dir_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), 'themes')) + +def find_theme(name): + # Where else to look for a theme? + # Check working dir? Destination dir? Config dir? Plugins dir? + path = os.path.join(themes_dir_path, name) + if not os.path.isdir(path): + raise docutils.ApplicationError( + 'Theme directory not found: %r (path: %r)' % (name, path)) + return path + + +class Writer(html4css1.Writer): + + settings_spec = html4css1.Writer.settings_spec + ( + 'S5 Slideshow Specific Options', + 'For the S5/HTML writer, the --no-toc-backlinks option ' + '(defined in General Docutils Options above) is the default, ' + 'and should not be changed.', + (('Specify an installed S5 theme by name. Overrides --theme-url. ' + 'The default theme name is "default". The theme files will be ' + 'copied into a "ui/" directory, in the same directory as the ' + 'destination file (output HTML). Note that existing theme files ' + 'will not be overwritten (unless --overwrite-theme-files is used).', + ['--theme'], + {'default': 'default', 'metavar': '', + 'overrides': 'theme_url'}), + ('Specify an S5 theme URL. The destination file (output HTML) will ' + 'link to this theme; nothing will be copied. Overrides --theme.', + ['--theme-url'], + {'metavar': '', 'overrides': 'theme'}), + ('Allow existing theme files in the ``ui/`` directory to be ' + 'overwritten. The default is not to overwrite theme files.', + ['--overwrite-theme-files'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Keep existing theme files in the ``ui/`` directory; do not ' + 'overwrite any. This is the default.', + ['--keep-theme-files'], + {'dest': 'overwrite_theme_files', 'action': 'store_false'}), + ('Set the initial view mode to "slideshow" [default] or "outline".', + ['--view-mode'], + {'choices': ['slideshow', 'outline'], 'default': 'slideshow', + 'metavar': ''}), + ('Normally hide the presentation controls in slideshow mode. ' + 'This is the default.', + ['--hidden-controls'], + {'action': 'store_true', 'default': True, + 'validator': frontend.validate_boolean}), + ('Always show the presentation controls in slideshow mode. ' + 'The default is to hide the controls.', + ['--visible-controls'], + {'dest': 'hidden_controls', 'action': 'store_false'}), + ('Enable the current slide indicator ("1 / 15"). ' + 'The default is to disable it.', + ['--current-slide'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Disable the current slide indicator. This is the default.', + ['--no-current-slide'], + {'dest': 'current_slide', 'action': 'store_false'}),)) + + settings_default_overrides = {'toc_backlinks': 0} + + config_section = 's5_html writer' + config_section_dependencies = ('writers', 'html4css1 writer') + + def __init__(self): + html4css1.Writer.__init__(self) + self.translator_class = S5HTMLTranslator + + +class S5HTMLTranslator(html4css1.HTMLTranslator): + + s5_stylesheet_template = """\ + + + + + + + + +\n""" + # The script element must go in front of the link elements to + # avoid a flash of unstyled content (FOUC), reproducible with + # Firefox. + + disable_current_slide = """ +\n""" + + layout_template = """\ +
+
+
+ + +
\n""" +#
+#
+#
+#
+ + default_theme = 'default' + """Name of the default theme.""" + + base_theme_file = '__base__' + """Name of the file containing the name of the base theme.""" + + direct_theme_files = ( + 'slides.css', 'outline.css', 'print.css', 'opera.css', 'slides.js') + """Names of theme files directly linked to in the output HTML""" + + indirect_theme_files = ( + 's5-core.css', 'framing.css', 'pretty.css', 'blank.gif', 'iepngfix.htc') + """Names of files used indirectly; imported or used by files in + `direct_theme_files`.""" + + required_theme_files = indirect_theme_files + direct_theme_files + """Names of mandatory theme files.""" + + def __init__(self, *args): + html4css1.HTMLTranslator.__init__(self, *args) + #insert S5-specific stylesheet and script stuff: + self.theme_file_path = None + self.setup_theme() + view_mode = self.document.settings.view_mode + control_visibility = ('visible', 'hidden')[self.document.settings + .hidden_controls] + self.stylesheet.append(self.s5_stylesheet_template + % {'path': self.theme_file_path, + 'view_mode': view_mode, + 'control_visibility': control_visibility}) + if not self.document.settings.current_slide: + self.stylesheet.append(self.disable_current_slide) + self.add_meta('\n') + self.s5_footer = [] + self.s5_header = [] + self.section_count = 0 + self.theme_files_copied = None + + def setup_theme(self): + if self.document.settings.theme: + self.copy_theme() + elif self.document.settings.theme_url: + self.theme_file_path = self.document.settings.theme_url + else: + raise docutils.ApplicationError( + 'No theme specified for S5/HTML writer.') + + def copy_theme(self): + """ + Locate & copy theme files. + + A theme may be explicitly based on another theme via a '__base__' + file. The default base theme is 'default'. Files are accumulated + from the specified theme, any base themes, and 'default'. + """ + settings = self.document.settings + path = find_theme(settings.theme) + theme_paths = [path] + self.theme_files_copied = {} + required_files_copied = {} + # This is a link (URL) in HTML, so we use "/", not os.sep: + self.theme_file_path = '%s/%s' % ('ui', settings.theme) + if settings._destination: + dest = os.path.join( + os.path.dirname(settings._destination), 'ui', settings.theme) + if not os.path.isdir(dest): + os.makedirs(dest) + else: + # no destination, so we can't copy the theme + return + default = False + while path: + for f in os.listdir(path): # copy all files from each theme + if f == self.base_theme_file: + continue # ... except the "__base__" file + if ( self.copy_file(f, path, dest) + and f in self.required_theme_files): + required_files_copied[f] = 1 + if default: + break # "default" theme has no base theme + # Find the "__base__" file in theme directory: + base_theme_file = os.path.join(path, self.base_theme_file) + # If it exists, read it and record the theme path: + if os.path.isfile(base_theme_file): + lines = open(base_theme_file).readlines() + for line in lines: + line = line.strip() + if line and not line.startswith('#'): + path = find_theme(line) + if path in theme_paths: # check for duplicates (cycles) + path = None # if found, use default base + else: + theme_paths.append(path) + break + else: # no theme name found + path = None # use default base + else: # no base theme file found + path = None # use default base + if not path: + path = find_theme(self.default_theme) + theme_paths.append(path) + default = True + if len(required_files_copied) != len(self.required_theme_files): + # Some required files weren't found & couldn't be copied. + required = list(self.required_theme_files) + for f in list(required_files_copied.keys()): + required.remove(f) + raise docutils.ApplicationError( + 'Theme files not found: %s' + % ', '.join(['%r' % f for f in required])) + + files_to_skip_pattern = re.compile(r'~$|\.bak$|#$|\.cvsignore$') + + def copy_file(self, name, source_dir, dest_dir): + """ + Copy file `name` from `source_dir` to `dest_dir`. + Return 1 if the file exists in either `source_dir` or `dest_dir`. + """ + source = os.path.join(source_dir, name) + dest = os.path.join(dest_dir, name) + if dest in self.theme_files_copied: + return 1 + else: + self.theme_files_copied[dest] = 1 + if os.path.isfile(source): + if self.files_to_skip_pattern.search(source): + return None + settings = self.document.settings + if os.path.exists(dest) and not settings.overwrite_theme_files: + settings.record_dependencies.add(dest) + else: + src_file = open(source, 'rb') + src_data = src_file.read() + src_file.close() + dest_file = open(dest, 'wb') + dest_dir = dest_dir.replace(os.sep, '/') + dest_file.write(src_data.replace( + b('ui/default'), + dest_dir[dest_dir.rfind('ui/'):].encode( + sys.getfilesystemencoding()))) + dest_file.close() + settings.record_dependencies.add(source) + return 1 + if os.path.isfile(dest): + return 1 + + def depart_document(self, node): + self.head_prefix.extend([self.doctype, + self.head_prefix_template % + {'lang': self.settings.language_code}]) + self.html_prolog.append(self.doctype) + self.meta.insert(0, self.content_type % self.settings.output_encoding) + self.head.insert(0, self.content_type % self.settings.output_encoding) + if self.math_header: + if self.math_output == 'mathjax': + self.head.extend(self.math_header) + else: + self.stylesheet.extend(self.math_header) + # skip content-type meta tag with interpolated charset value: + self.html_head.extend(self.head[1:]) + self.fragment.extend(self.body) + # special S5 code up to the next comment line + header = ''.join(self.s5_header) + footer = ''.join(self.s5_footer) + title = ''.join(self.html_title).replace('

', '

') + layout = self.layout_template % {'header': header, + 'title': title, + 'footer': footer} + self.body_prefix.extend(layout) + self.body_prefix.append('
\n') + self.body_prefix.append( + self.starttag({'classes': ['slide'], 'ids': ['slide0']}, 'div')) + if not self.section_count: + self.body.append('
\n') + # + self.body_suffix.insert(0, '

\n') + self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo + + self.docinfo + self.body + + self.body_suffix[:-1]) + + def depart_footer(self, node): + start = self.context.pop() + self.s5_footer.append('

') + self.s5_footer.extend(self.body[start:]) + self.s5_footer.append('

') + del self.body[start:] + + def depart_header(self, node): + start = self.context.pop() + header = ['\n') + del self.body[start:] + self.s5_header.extend(header) + + def visit_section(self, node): + if not self.section_count: + self.body.append('\n\n') + self.section_count += 1 + self.section_level += 1 + if self.section_level > 1: + # dummy for matching div's + self.body.append(self.starttag(node, 'div', CLASS='section')) + else: + self.body.append(self.starttag(node, 'div', CLASS='slide')) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.section): + level = self.section_level + self.initial_header_level - 1 + if level == 1: + level = 2 + tag = 'h%s' % level + self.body.append(self.starttag(node, tag, '')) + self.context.append('\n' % tag) + else: + html4css1.HTMLTranslator.visit_subtitle(self, node) + + def visit_title(self, node): + html4css1.HTMLTranslator.visit_title(self, node) diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/README.txt b/python/helpers/py3only/docutils/writers/s5_html/themes/README.txt new file mode 100644 index 000000000000..2e01b51ee78a --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/README.txt @@ -0,0 +1,6 @@ +Except where otherwise noted (default/iepngfix.htc), all files in this +directory have been released into the Public Domain. + +These files are based on files from S5 1.1, released into the Public +Domain by Eric Meyer. For further details, please see +http://www.meyerweb.com/eric/tools/s5/credits.html. diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/__base__ b/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/__base__ new file mode 100644 index 000000000000..f08be9ad5989 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/__base__ @@ -0,0 +1,2 @@ +# base theme of this theme: +big-white diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/framing.css b/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/framing.css new file mode 100644 index 000000000000..5a31113fb4eb --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/framing.css @@ -0,0 +1,25 @@ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {top: 0; z-index: 1;} +div#footer {display:none;} +.slide {top: 0; width: 92%; padding: 0.1em 4% 4%; z-index: 2;} +/* list-style: none;} */ +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/pretty.css b/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/pretty.css new file mode 100644 index 000000000000..82bcc9dc432d --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/big-black/pretty.css @@ -0,0 +1,109 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: black; color: white;} +:link, :visited {text-decoration: none; color: cyan;} +#controls :active {color: #888 !important;} +#controls :focus {outline: 1px dotted #CCC;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +.slide {font-size: 3em; font-family: sans-serif; font-weight: bold;} +.slide h1 {padding-top: 0; z-index: 1; margin: 0; font-size: 120%;} +.slide h2 {font-size: 110%;} +.slide h3 {font-size: 105%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + border: none; color: #888; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: black; color: #CCC;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #AAA; + font-family: sans-serif; font-weight: bold;} + +#slide0 h1 {position: static; margin: 0 0 0.5em; padding-top: 0.3em; top: 0; + font-size: 150%; white-space: normal; background: transparent;} +#slide0 h2 {font: 110%; font-style: italic; color: gray;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #FCC;} + +.incremental, .incremental *, .incremental *:after { + color: black; visibility: visible; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: lime;} + +.slide-display {display: inline ! important;} + +.huge {font-size: 150%;} +.big {font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 0.5em 0 0.5em 1em;} + +div.sidebar {background-color: black;} + +pre.literal-block, pre.doctest-block {background-color: black;} + +tt.docutils {background-color: black;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/big-white/framing.css b/python/helpers/py3only/docutils/writers/s5_html/themes/big-white/framing.css new file mode 100644 index 000000000000..cd343432b367 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/big-white/framing.css @@ -0,0 +1,24 @@ +/* This file has been placed in the public domain. */ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#footer {display:none;} +.slide {top: 0; width: 92%; padding: 0.25em 4% 4%; z-index: 2;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/big-white/pretty.css b/python/helpers/py3only/docutils/writers/s5_html/themes/big-white/pretty.css new file mode 100644 index 000000000000..c5e2fcf97a63 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/big-white/pretty.css @@ -0,0 +1,107 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: white; color: black;} +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +.slide {font-size: 3em; font-family: sans-serif; font-weight: bold;} +.slide h1 {padding-top: 0; z-index: 1; margin: 0; font-size: 120%;} +.slide h2 {font-size: 110%;} +.slide h3 {font-size: 105%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + border: none; color: #005; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #444; + font-family: sans-serif; font-weight: bold;} + +#slide0 h1 {position: static; margin: 0 0 0.5em; padding-top: 0.3em; top: 0; + font-size: 150%; white-space: normal; background: transparent;} +#slide0 h2 {font: 110%; font-style: italic; color: gray;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after { + color: white; visibility: visible; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: green;} + +.slide-display {display: inline ! important;} + +.huge {font-size: 150%;} +.big {font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 0.5em 0 0.5em 1em;} + +pre.literal-block, pre.doctest-block {background-color: white;} + +tt.docutils {background-color: white;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/blank.gif b/python/helpers/py3only/docutils/writers/s5_html/themes/default/blank.gif new file mode 100644 index 000000000000..75b945d25538 Binary files /dev/null and b/python/helpers/py3only/docutils/writers/s5_html/themes/default/blank.gif differ diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/framing.css b/python/helpers/py3only/docutils/writers/s5_html/themes/default/framing.css new file mode 100644 index 000000000000..c4727f303c32 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/framing.css @@ -0,0 +1,25 @@ +/* This file has been placed in the public domain. */ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {position: fixed; top: 0; height: 3em; z-index: 1;} +div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} +.slide {top: 0; width: 92%; padding: 2.5em 4% 4%; z-index: 2;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/iepngfix.htc b/python/helpers/py3only/docutils/writers/s5_html/themes/default/iepngfix.htc new file mode 100644 index 000000000000..9f3d628b5da9 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/iepngfix.htc @@ -0,0 +1,42 @@ + + + + + \ No newline at end of file diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/opera.css b/python/helpers/py3only/docutils/writers/s5_html/themes/default/opera.css new file mode 100644 index 000000000000..c9d1148bed27 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/opera.css @@ -0,0 +1,8 @@ +/* This file has been placed in the public domain. */ +/* DO NOT CHANGE THESE unless you really want to break Opera Show */ +.slide { + visibility: visible !important; + position: static !important; + page-break-before: always; +} +#slide0 {page-break-before: avoid;} diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/outline.css b/python/helpers/py3only/docutils/writers/s5_html/themes/default/outline.css new file mode 100644 index 000000000000..fa767e227e63 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/outline.css @@ -0,0 +1,16 @@ +/* This file has been placed in the public domain. */ +/* Don't change this unless you want the layout stuff to show up in the + outline view! */ + +.layout div, #footer *, #controlForm * {display: none;} +#footer, #controls, #controlForm, #navLinks, #toggle { + display: block; visibility: visible; margin: 0; padding: 0;} +#toggle {float: right; padding: 0.5em;} +html>body #toggle {position: fixed; top: 0; right: 0;} + +/* making the outline look pretty-ish */ + +#slide0 h1, #slide0 h2, #slide0 h3, #slide0 h4 {border: none; margin: 0;} +#toggle {border: 1px solid; border-width: 0 0 1px 1px; background: #FFF;} + +.outline {display: inline ! important;} diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/pretty.css b/python/helpers/py3only/docutils/writers/s5_html/themes/default/pretty.css new file mode 100644 index 000000000000..1cede72d4a87 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/pretty.css @@ -0,0 +1,120 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: white; color: black;} +/* Replace the background style above with the style below (and again for + div#header) for a graphic: */ +/* background: white url(bodybg.gif) -16px 0 no-repeat; */ +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} +h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +div#header, div#footer {background: #005; color: #AAB; font-family: sans-serif;} +/* background: #005 url(bodybg.gif) -16px 0 no-repeat; */ +div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} +#footer h1 {display: block; padding: 0 1em;} +#footer h2 {display: block; padding: 0.8em 1em 0;} + +.slide {font-size: 1.2em;} +.slide h1 {position: absolute; top: 0.45em; z-index: 1; + margin: 0; padding-left: 0.7em; white-space: nowrap; + font: bold 150% sans-serif; color: #DDE; background: #005;} +.slide h2 {font: bold 120%/1em sans-serif; padding-top: 0.5em;} +.slide h3 {font: bold 100% sans-serif; padding-top: 0.5em;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + background: #005; border: none; color: #779; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #449; + font-family: sans-serif; font-weight: bold;} + +#slide0 {padding-top: 1.5em} +#slide0 h1 {position: static; margin: 1em 0 0; padding: 0; color: #000; + font: bold 2em sans-serif; white-space: normal; background: transparent;} +#slide0 h2 {font: bold italic 1em sans-serif; margin: 0.25em;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after {visibility: visible; + color: white; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: green;} + +.slide-display {display: inline ! important;} + +.huge {font-family: sans-serif; font-weight: bold; font-size: 150%;} +.big {font-family: sans-serif; font-weight: bold; font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 1em 0 0.5em 2em;} + +pre.literal-block, pre.doctest-block {background-color: white;} + +tt.docutils {background-color: white;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/print.css b/python/helpers/py3only/docutils/writers/s5_html/themes/default/print.css new file mode 100644 index 000000000000..9d057cc8c17c --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/print.css @@ -0,0 +1,24 @@ +/* This file has been placed in the public domain. */ +/* The following rule is necessary to have all slides appear in print! + DO NOT REMOVE IT! */ +.slide, ul {page-break-inside: avoid; visibility: visible !important;} +h1 {page-break-after: avoid;} + +body {font-size: 12pt; background: white;} +* {color: black;} + +#slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;} +#slide0 h3 {margin: 0; padding: 0;} +#slide0 h4 {margin: 0 0 0.5em; padding: 0;} +#slide0 {margin-bottom: 3em;} + +#header {display: none;} +#footer h1 {margin: 0; border-bottom: 1px solid; color: gray; + font-style: italic;} +#footer h2, #controls {display: none;} + +.print {display: inline ! important;} + +/* The following rule keeps the layout stuff out of print. + Remove at your own risk! */ +.layout, .layout * {display: none !important;} diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/s5-core.css b/python/helpers/py3only/docutils/writers/s5_html/themes/default/s5-core.css new file mode 100644 index 000000000000..6965f5e8f877 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/s5-core.css @@ -0,0 +1,11 @@ +/* This file has been placed in the public domain. */ +/* Do not edit or override these styles! + The system will likely break if you do. */ + +div#header, div#footer, div#controls, .slide {position: absolute;} +html>body div#header, html>body div#footer, + html>body div#controls, html>body .slide {position: fixed;} +.handout {display: none;} +.layout {display: block;} +.slide, .hideme, .incremental {visibility: hidden;} +#slide0 {visibility: visible;} diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/slides.css b/python/helpers/py3only/docutils/writers/s5_html/themes/default/slides.css new file mode 100644 index 000000000000..82bdc0ee0830 --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/slides.css @@ -0,0 +1,10 @@ +/* This file has been placed in the public domain. */ + +/* required to make the slide show run at all */ +@import url(s5-core.css); + +/* sets basic placement and size of slide components */ +@import url(framing.css); + +/* styles that make the slides look good */ +@import url(pretty.css); diff --git a/python/helpers/py3only/docutils/writers/s5_html/themes/default/slides.js b/python/helpers/py3only/docutils/writers/s5_html/themes/default/slides.js new file mode 100644 index 000000000000..2ebbbd8e3e7a --- /dev/null +++ b/python/helpers/py3only/docutils/writers/s5_html/themes/default/slides.js @@ -0,0 +1,558 @@ +// S5 v1.1 slides.js -- released into the Public Domain +// Modified for Docutils (http://docutils.sf.net) by David Goodger +// +// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for +// information about all the wonderful and talented contributors to this code! + +var undef; +var slideCSS = ''; +var snum = 0; +var smax = 1; +var slideIDs = []; +var incpos = 0; +var number = undef; +var s5mode = true; +var defaultView = 'slideshow'; +var controlVis = 'visible'; + +var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0; +var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; +var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0; + +function hasClass(object, className) { + if (!object.className) return false; + return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1); +} + +function hasValue(object, value) { + if (!object) return false; + return (object.search('(^|\\s)' + value + '(\\s|$)') != -1); +} + +function removeClass(object,className) { + if (!object) return; + object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2); +} + +function addClass(object,className) { + if (!object || hasClass(object, className)) return; + if (object.className) { + object.className += ' '+className; + } else { + object.className = className; + } +} + +function GetElementsWithClassName(elementName,className) { + var allElements = document.getElementsByTagName(elementName); + var elemColl = []; + for (var i = 0; i< allElements.length; i++) { + if (hasClass(allElements[i], className)) { + elemColl[elemColl.length] = allElements[i]; + } + } + return elemColl; +} + +function isParentOrSelf(element, id) { + if (element == null || element.nodeName=='BODY') return false; + else if (element.id == id) return true; + else return isParentOrSelf(element.parentNode, id); +} + +function nodeValue(node) { + var result = ""; + if (node.nodeType == 1) { + var children = node.childNodes; + for (var i = 0; i < children.length; ++i) { + result += nodeValue(children[i]); + } + } + else if (node.nodeType == 3) { + result = node.nodeValue; + } + return(result); +} + +function slideLabel() { + var slideColl = GetElementsWithClassName('*','slide'); + var list = document.getElementById('jumplist'); + smax = slideColl.length; + for (var n = 0; n < smax; n++) { + var obj = slideColl[n]; + + var did = 'slide' + n.toString(); + if (obj.getAttribute('id')) { + slideIDs[n] = obj.getAttribute('id'); + } + else { + obj.setAttribute('id',did); + slideIDs[n] = did; + } + if (isOp) continue; + + var otext = ''; + var menu = obj.firstChild; + if (!menu) continue; // to cope with empty slides + while (menu && menu.nodeType == 3) { + menu = menu.nextSibling; + } + if (!menu) continue; // to cope with slides with only text nodes + + var menunodes = menu.childNodes; + for (var o = 0; o < menunodes.length; o++) { + otext += nodeValue(menunodes[o]); + } + list.options[list.length] = new Option(n + ' : ' + otext, n); + } +} + +function currentSlide() { + var cs; + var footer_nodes; + var vis = 'visible'; + if (document.getElementById) { + cs = document.getElementById('currentSlide'); + footer_nodes = document.getElementById('footer').childNodes; + } else { + cs = document.currentSlide; + footer = document.footer.childNodes; + } + cs.innerHTML = '' + snum + '<\/span> ' + + '\/<\/span> ' + + '' + (smax-1) + '<\/span>'; + if (snum == 0) { + vis = 'hidden'; + } + cs.style.visibility = vis; + for (var i = 0; i < footer_nodes.length; i++) { + if (footer_nodes[i].nodeType == 1) { + footer_nodes[i].style.visibility = vis; + } + } +} + +function go(step) { + if (document.getElementById('slideProj').disabled || step == 0) return; + var jl = document.getElementById('jumplist'); + var cid = slideIDs[snum]; + var ce = document.getElementById(cid); + if (incrementals[snum].length > 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + removeClass(incrementals[snum][i], 'current'); + removeClass(incrementals[snum][i], 'incremental'); + } + } + if (step != 'j') { + snum += step; + lmax = smax - 1; + if (snum > lmax) snum = lmax; + if (snum < 0) snum = 0; + } else + snum = parseInt(jl.value); + var nid = slideIDs[snum]; + var ne = document.getElementById(nid); + if (!ne) { + ne = document.getElementById(slideIDs[0]); + snum = 0; + } + if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;} + if (incrementals[snum].length > 0 && incpos == 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + if (hasClass(incrementals[snum][i], 'current')) + incpos = i + 1; + else + addClass(incrementals[snum][i], 'incremental'); + } + } + if (incrementals[snum].length > 0 && incpos > 0) + addClass(incrementals[snum][incpos - 1], 'current'); + ce.style.visibility = 'hidden'; + ne.style.visibility = 'visible'; + jl.selectedIndex = snum; + currentSlide(); + number = 0; +} + +function goTo(target) { + if (target >= smax || target == snum) return; + go(target - snum); +} + +function subgo(step) { + if (step > 0) { + removeClass(incrementals[snum][incpos - 1],'current'); + removeClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos],'current'); + incpos++; + } else { + incpos--; + removeClass(incrementals[snum][incpos],'current'); + addClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos - 1],'current'); + } +} + +function toggle() { + var slideColl = GetElementsWithClassName('*','slide'); + var slides = document.getElementById('slideProj'); + var outline = document.getElementById('outlineStyle'); + if (!slides.disabled) { + slides.disabled = true; + outline.disabled = false; + s5mode = false; + fontSize('1em'); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'visible'; + } + } else { + slides.disabled = false; + outline.disabled = true; + s5mode = true; + fontScale(); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'hidden'; + } + slideColl[snum].style.visibility = 'visible'; + } +} + +function showHide(action) { + var obj = GetElementsWithClassName('*','hideme')[0]; + switch (action) { + case 's': obj.style.visibility = 'visible'; break; + case 'h': obj.style.visibility = 'hidden'; break; + case 'k': + if (obj.style.visibility != 'visible') { + obj.style.visibility = 'visible'; + } else { + obj.style.visibility = 'hidden'; + } + break; + } +} + +// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) +function keys(key) { + if (!key) { + key = event; + key.which = key.keyCode; + } + if (key.which == 84) { + toggle(); + return; + } + if (s5mode) { + switch (key.which) { + case 10: // return + case 13: // enter + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + if(number != undef) { + goTo(number); + break; + } + case 32: // spacebar + case 34: // page down + case 39: // rightkey + case 40: // downkey + if(number != undef) { + go(number); + } else if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + break; + case 33: // page up + case 37: // leftkey + case 38: // upkey + if(number != undef) { + go(-1 * number); + } else if (!incrementals[snum] || incpos <= 0) { + go(-1); + } else { + subgo(-1); + } + break; + case 36: // home + goTo(0); + break; + case 35: // end + goTo(smax-1); + break; + case 67: // c + showHide('k'); + break; + } + if (key.which < 48 || key.which > 57) { + number = undef; + } else { + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + number = (((number != undef) ? number : 0) * 10) + (key.which - 48); + } + } + return false; +} + +function clicker(e) { + number = undef; + var target; + if (window.event) { + target = window.event.srcElement; + e = window.event; + } else target = e.target; + if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true; + if (!e.which || e.which == 1) { + if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + } +} + +function findSlide(hash) { + var target = document.getElementById(hash); + if (target) { + for (var i = 0; i < slideIDs.length; i++) { + if (target.id == slideIDs[i]) return i; + } + } + return null; +} + +function slideJump() { + if (window.location.hash == null || window.location.hash == '') { + currentSlide(); + return; + } + if (window.location.hash == null) return; + var dest = null; + dest = findSlide(window.location.hash.slice(1)); + if (dest == null) { + dest = 0; + } + go(dest - snum); +} + +function fixLinks() { + var thisUri = window.location.href; + thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length); + var aelements = document.getElementsByTagName('A'); + for (var i = 0; i < aelements.length; i++) { + var a = aelements[i].href; + var slideID = a.match('\#.+'); + if ((slideID) && (slideID[0].slice(0,1) == '#')) { + var dest = findSlide(slideID[0].slice(1)); + if (dest != null) { + if (aelements[i].addEventListener) { + aelements[i].addEventListener("click", new Function("e", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "if (e.preventDefault) e.preventDefault();"), true); + } else if (aelements[i].attachEvent) { + aelements[i].attachEvent("onclick", new Function("", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "event.returnValue = false;")); + } + } + } + } +} + +function externalLinks() { + if (!document.getElementsByTagName) return; + var anchors = document.getElementsByTagName('a'); + for (var i=0; i' + + '